diff options
author | Clyne Sullivan <tullivan99@gmail.com> | 2016-11-08 20:09:05 -0500 |
---|---|---|
committer | Clyne Sullivan <tullivan99@gmail.com> | 2016-11-08 20:09:05 -0500 |
commit | 92235fb259b0ebdfc99859c2c95fe1f8c163f411 (patch) | |
tree | e90d9b5adddad8d0ae744eadb5d15313517cc71b | |
parent | daad5eaa0296ff30624da5fbfaacdb88792b5fea (diff) |
trying out distortos
125 files changed, 16100 insertions, 1774 deletions
diff --git a/Makefile b/Makefile deleted file mode 100644 index 5a7aaed..0000000 --- a/Makefile +++ /dev/null @@ -1,239 +0,0 @@ -##############################################################################
-# Build global options
-# NOTE: Can be overridden externally.
-#
-include setup.mk
-
-# Compiler options here.
-ifeq ($(USE_OPT),)
- USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16 -fsingle-precision-constant
-endif
-
-# C specific options here (added to USE_OPT).
-ifeq ($(USE_COPT),)
- USE_COPT = -std=gnu99
-endif
-
-# C++ specific options here (added to USE_OPT).
-ifeq ($(USE_CPPOPT),)
- USE_CPPOPT = -fno-rtti
-endif
-
-# Enable this if you want the linker to remove unused code and data
-ifeq ($(USE_LINK_GC),)
- USE_LINK_GC = yes
-endif
-
-# If enabled, this option allows to compile the application in THUMB mode.
-ifeq ($(USE_THUMB),)
- USE_THUMB = yes
-endif
-
-# Enable this if you want to see the full log while compiling.
-ifeq ($(USE_VERBOSE_COMPILE),)
- USE_VERBOSE_COMPILE = yes
-endif
-
-#
-# Build global options
-##############################################################################
-
-##############################################################################
-# Architecture or project specific options
-#
-
-# Enable this if you really want to use the STM FWLib.
-ifeq ($(USE_FWLIB),)
- USE_FWLIB = no
-endif
-
-#
-# Architecture or project specific options
-##############################################################################
-
-##############################################################################
-# Project, sources and paths
-#
-
-# PROS compatible build directory
-ifeq ($(BUILDDIR),)
-BUILDDIR = bin
-endif
-
-# Define project name here
-ifeq ($(PROJECT),)
-PROJECT = output
-endif
-
-# Path to ChibiOS/RT - default assumes making examples
-ifeq ($(CHIBIOS),)
-CHIBIOS = ../../../../ChibiOS_2.6.2
-endif
-
-# Path to ConVEX root - default assumes making examples
-ifeq ($(CONVEX),)
-CONVEX = ../..
-endif
-
-# Imported source files and paths
-include $(CONVEX)/boards/VEX_STM32_CORTEX/board.mk
-include $(CHIBIOS)/os/hal/platforms/STM32F1xx/platform.mk
-include $(CHIBIOS)/os/hal/hal.mk
-include $(CHIBIOS)/os/ports/GCC/ARMCMx/STM32F1xx/port.mk
-include $(CHIBIOS)/os/kernel/kernel.mk
-include $(CONVEX)/fw/vexfw.mk
-
-# include the optional code
-ifeq ($(CONVEX_OPT),yes)
-include $(CONVEX)/opt/vexopt.mk
-endif
-
-# Define linker script file here
-LDSCRIPT= $(CONVEX)/ld/STM32F103xD.ld
-
-
-# C sources that can be compiled in ARM or THUMB mode depending on the global
-# setting.
-# replaced standard shell with custom variant
-# $(CHIBIOS)/os/various/shell.c \
-
-CSRC = $(PORTSRC) \
- $(KERNSRC) \
- $(HALSRC) \
- $(PLATFORMSRC) \
- $(BOARDSRC) \
- $(CHIBIOS)/os/various/evtimer.c \
- $(CHIBIOS)/os/various/syscalls.c \
- $(CHIBIOS)/os/various/chprintf.c \
- $(VEXFWSRC) \
- $(VEXOPTSRC)
-# $(VEXUSERSRC) \
-# main.c
-
-# C++ sources that can be compiled in ARM or THUMB mode depending on the global
-# setting.
-CPPSRC = $(VEXUSERSRC) \
- main.cpp
-
-# C sources to be compiled in ARM mode regardless of the global setting.
-# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
-# option that results in lower performance and larger code size.
-ACSRC =
-
-# C++ sources to be compiled in ARM mode regardless of the global setting.
-# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
-# option that results in lower performance and larger code size.
-ACPPSRC =
-
-# C sources to be compiled in THUMB mode regardless of the global setting.
-# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
-# option that results in lower performance and larger code size.
-TCSRC =
-
-# C sources to be compiled in THUMB mode regardless of the global setting.
-# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
-# option that results in lower performance and larger code size.
-TCPPSRC =
-
-# List ASM source files here
-ASMSRC = $(PORTASM)
-
-INCDIR = $(PORTINC) $(KERNINC) \
- $(HALINC) $(PLATFORMINC) $(BOARDINC) \
- $(CHIBIOS)/os/various $(VEXFWINC) $(VEXOPTINC) $(VEXUSERINC)
-
-#
-# Project, sources and paths
-##############################################################################
-
-##############################################################################
-# Compiler settings
-#
-
-MCU = cortex-m3
-
-#TRGT = arm-elf-
-TRGT = arm-none-eabi-
-CC = $(TRGT)gcc
-CPPC = $(TRGT)g++
-# Enable loading with g++ only if you need C++ runtime support.
-# NOTE: You can use C++ even without C++ support if you are careful. C++
-# runtime support makes code size explode.
-#D = $(TRGT)gcc
-LD = $(TRGT)g++
-CP = $(TRGT)objcopy
-AS = $(TRGT)gcc -x assembler-with-cpp
-OD = $(TRGT)objdump
-HEX = $(CP) -O ihex
-BIN = $(CP) -O binary
-
-# ARM-specific options here
-AOPT = -mfloat-abi=hard -mcpu=cortex-m3
-
-# THUMB-specific options here
-TOPT = -mthumb -DTHUMB
-
-# Define C warning options here
-CWARN = -Wall -Wextra -Wstrict-prototypes
-
-# Define C++ warning options here
-CPPWARN = -Wall -Wextra -Wno-write-strings
-
-#
-# Compiler settings
-##############################################################################
-
-##############################################################################
-# Start of default section
-#
-
-# List all default C defines here, like -D_DEBUG=1
-DDEFS =
-
-# List all default ASM defines here, like -D_DEBUG=1
-DADEFS =
-
-# List all default directories to look for include files here
-DINCDIR =
-
-# List the default directory to look for the libraries here
-DLIBDIR =
-
-# List all default libraries here
-DLIBS =
-
-#
-# End of default section
-##############################################################################
-
-##############################################################################
-# Start of user section
-#
-
-# List all user C define here, like -D_DEBUG=1
-UDEFS =
-
-# Define ASM defines here
-UADEFS =
-
-# List all user directories here
-UINCDIR =
-
-# List the user directory to look for the libraries here
-ULIBDIR =
-
-# List all user libraries here
-ULIBS = -lm
-
-#
-# End of user defines
-##############################################################################
-
-ifeq ($(USE_FWLIB),yes)
- include $(CHIBIOS)/ext/stm32lib/stm32lib.mk
- CSRC += $(STM32SRC)
- INCDIR += $(STM32INC)
- USE_OPT += -DUSE_STDPERIPH_DRIVER
-endif
-
-include $(CHIBIOS)/os/ports/GCC/ARMCMx/rules.mk
diff --git a/STM32F103VD.ld b/STM32F103VD.ld new file mode 100644 index 0000000..21f2004 --- /dev/null +++ b/STM32F103VD.ld @@ -0,0 +1,267 @@ +/** + * \file + * \brief Linker script for STM32F103VD chip: + * - 393216 bytes of rom; + * - 65536 bytes of ram; + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + * + * \warning + * Automatically generated file - do not edit! + * + * \date 2016-11-08 19:50:43 + */ + +SEARCH_DIR(.); +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm"); +OUTPUT_ARCH(arm); + +/*---------------------------------------------------------------------------------------------------------------------+ +| stacks sizes ++---------------------------------------------------------------------------------------------------------------------*/ + +/* Handler mode (core exceptions / interrupts) can use only main stack */ +PROVIDE(__main_stack_size = 2048); + +/* Thread mode can use main stack (default after reset) or process stack - selected in CONTROL special register */ +PROVIDE(__process_stack_size = 2048); + +/*---------------------------------------------------------------------------------------------------------------------+ +| available memories ++---------------------------------------------------------------------------------------------------------------------*/ + +MEMORY +{ + rom : org = 0x08000000, len = 393216 + ram : org = 0x20000000, len = 65536 +} + +PROVIDE(__rom_start = ORIGIN(rom)); +PROVIDE(__rom_size = LENGTH(rom)); +PROVIDE(__rom_end = __rom_start + __rom_size); + +PROVIDE(__ram_start = ORIGIN(ram)); +PROVIDE(__ram_size = LENGTH(ram)); +PROVIDE(__ram_end = __ram_start + __ram_size); + +/*---------------------------------------------------------------------------------------------------------------------+ +| entry point ++---------------------------------------------------------------------------------------------------------------------*/ + +ENTRY(Reset_Handler); + +/*---------------------------------------------------------------------------------------------------------------------+ +| put data in sections ++---------------------------------------------------------------------------------------------------------------------*/ + +SECTIONS +{ + .text : + { + . = ALIGN(4); + PROVIDE(__text_start = .); + + /* sub-section: .vectors */ + + . = ALIGN(4); + PROVIDE(__vectors_start = .); + + KEEP(*(.coreVectors)); + KEEP(*(.chipVectors)); + + . = ALIGN(4); + PROVIDE(__vectors_end = .); + + /* end of sub-section: .vectors */ + + *(.text* .gnu.linkonce.t.*); + *(.rodata* .gnu.linkonce.r.*); + *(.glue_7t .glue_7); + + *(.ARM.extab* .gnu.linkonce.armextab.*); /* exception unwinding information */ + *(.gcc_except_table); /* information used for stack unwinding during exception */ + *(.eh_frame_hdr); /* additional information about .ex_frame section */ + *(.eh_frame); /* information used for stack unwinding during exception */ + + /* sub-section: data_array */ + + . = ALIGN(4); + PROVIDE(__data_array_start = .); + + LONG(LOADADDR(.data)); LONG(ADDR(.data)); LONG(ADDR(.data) + SIZEOF(.data)); + + . = ALIGN(4); + PROVIDE(__data_array_end = .); + + /* end of sub-section: data_array */ + + /* sub-section: bss_array */ + + . = ALIGN(4); + PROVIDE(__bss_array_start = .); + + LONG(ADDR(.bss)); LONG(ADDR(.bss) + SIZEOF(.bss)); + LONG(ADDR(.stack)); LONG(ADDR(.stack) + SIZEOF(.stack)); + + . = ALIGN(4); + PROVIDE(__bss_array_end = .); + + /* end of sub-section: bss_array */ + + /* sub-sections: init, preinit_array, init_array and fini_array */ + + KEEP(*(.init)); + + . = ALIGN(4); + PROVIDE(__preinit_array_start = .); + + KEEP(*(.preinit_array)); + + . = ALIGN(4); + PROVIDE(__preinit_array_end = .); + + . = ALIGN(4); + PROVIDE(__init_array_start = .); + + KEEP(*(SORT(.init_array.*))); + KEEP(*(.init_array)); + + . = ALIGN(4); + PROVIDE(__init_array_end = .); + + KEEP(*(.fini)); + + . = ALIGN(4); + PROVIDE(__fini_array_start = .); + + KEEP(*(.fini_array)); + KEEP(*(SORT(.fini_array.*))); + + . = ALIGN(4); + PROVIDE(__fini_array_end = .); + + /* end of sub-sections: init, preinit_array, init_array and fini_array */ + + . = ALIGN(4); + PROVIDE(__text_end = .); + } > rom AT > rom + + . = ALIGN(4); + PROVIDE(__exidx_start = .); + + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*); + } > rom AT > rom /* index entries for section unwinding */ + + . = ALIGN(4); + PROVIDE(__exidx_end = .); + + .bss : + { + . = ALIGN(4); + PROVIDE(__bss_start = .); + + *(.bss* .gnu.linkonce.b.*) + *(COMMON); + + . = ALIGN(4); + PROVIDE(__bss_end = .); + } > ram AT > ram + + .data : + { + . = ALIGN(4); + PROVIDE(__data_init_start = LOADADDR(.data)); + PROVIDE(__data_start = .); + + *(.data* .gnu.linkonce.d.*) + + . = ALIGN(4); + PROVIDE(__data_end = .); + } > ram AT > rom + + .noinit (NOLOAD) : + { + . = ALIGN(4); + PROVIDE(__noinit_start = .); + + *(.noinit) + + . = ALIGN(4); + PROVIDE(__noinit_end = .); + } > ram AT > ram + + .stack : + { + . = ALIGN(8); + PROVIDE(__stack_start = .); + PROVIDE(__main_stack_start = .); + + . += __main_stack_size; + + . = ALIGN(8); + PROVIDE(__main_stack_end = .); + PROVIDE(__process_stack_start = .); + + . += __process_stack_size; + + . = ALIGN(8); + PROVIDE(__process_stack_end = .); + PROVIDE(__stack_end = .); + } > ram AT > ram + + . = ALIGN(4); + PROVIDE(__heap_start = .); + PROVIDE(__heap_end = __ram_end); + + .stab 0 (NOLOAD) : { *(.stab) } + .stabstr 0 (NOLOAD) : { *(.stabstr) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to the beginning + * of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + .note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) } + .ARM.attributes 0 : { KEEP(*(.ARM.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) } +} + +PROVIDE(__text_size = __text_end - __text_start); +PROVIDE(__vectors_size = __vectors_end - __vectors_start); +PROVIDE(__data_array_size = __data_array_end - __data_array_start); +PROVIDE(__bss_array_size = __bss_array_end - __bss_array_start); +PROVIDE(__exidx_size = __exidx_end - __exidx_start); +PROVIDE(__bss_size = __bss_end - __bss_start); +PROVIDE(__data_size = __data_end - __data_start); +PROVIDE(__noinit_size = __noinit_end - __noinit_start); +PROVIDE(__stack_size = __stack_end - __stack_start); +PROVIDE(__heap_size = __heap_end - __heap_start); + +PROVIDE(__bss_start__ = __bss_start); +PROVIDE(__bss_end__ = __bss_end); diff --git a/chconf.h b/chconf.h deleted file mode 100644 index f943ea8..0000000 --- a/chconf.h +++ /dev/null @@ -1,531 +0,0 @@ -/*
- ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
-
- 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.
-*/
-
-/**
- * @file templates/chconf.h
- * @brief Configuration file template.
- * @details A copy of this file must be placed in each project directory, it
- * contains the application specific kernel settings.
- *
- * @addtogroup config
- * @details Kernel related settings and hooks.
- * @{
- */
-
-#ifndef _CHCONF_H_
-#define _CHCONF_H_
-
-/*===========================================================================*/
-/**
- * @name Kernel parameters and options
- * @{
- */
-/*===========================================================================*/
-
-/**
- * @brief System tick frequency.
- * @details Frequency of the system timer that drives the system ticks. This
- * setting also defines the system tick time unit.
- */
-#if !defined(CH_FREQUENCY) || defined(__DOXYGEN__)
-#define CH_FREQUENCY 1000
-#endif
-
-/**
- * @brief Round robin interval.
- * @details This constant is the number of system ticks allowed for the
- * threads before preemption occurs. Setting this value to zero
- * disables the preemption for threads with equal priority and the
- * round robin becomes cooperative. Note that higher priority
- * threads can still preempt, the kernel is always preemptive.
- *
- * @note Disabling the round robin preemption makes the kernel more compact
- * and generally faster.
- */
-#if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__)
-#define CH_TIME_QUANTUM 20
-#endif
-
-/**
- * @brief Managed RAM size.
- * @details Size of the RAM area to be managed by the OS. If set to zero
- * then the whole available RAM is used. The core memory is made
- * available to the heap allocator and/or can be used directly through
- * the simplified core memory allocator.
- *
- * @note In order to let the OS manage the whole RAM the linker script must
- * provide the @p __heap_base__ and @p __heap_end__ symbols.
- * @note Requires @p CH_USE_MEMCORE.
- */
-#if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__)
-#define CH_MEMCORE_SIZE 0
-#endif
-
-/**
- * @brief Idle thread automatic spawn suppression.
- * @details When this option is activated the function @p chSysInit()
- * does not spawn the idle thread automatically. The application has
- * then the responsibility to do one of the following:
- * - Spawn a custom idle thread at priority @p IDLEPRIO.
- * - Change the main() thread priority to @p IDLEPRIO then enter
- * an endless loop. In this scenario the @p main() thread acts as
- * the idle thread.
- * .
- * @note Unless an idle thread is spawned the @p main() thread must not
- * enter a sleep state.
- */
-#if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__)
-#define CH_NO_IDLE_THREAD FALSE
-#endif
-
-/** @} */
-
-/*===========================================================================*/
-/**
- * @name Performance options
- * @{
- */
-/*===========================================================================*/
-
-/**
- * @brief OS optimization.
- * @details If enabled then time efficient rather than space efficient code
- * is used when two possible implementations exist.
- *
- * @note This is not related to the compiler optimization options.
- * @note The default is @p TRUE.
- */
-#if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__)
-#define CH_OPTIMIZE_SPEED TRUE
-#endif
-
-/** @} */
-
-/*===========================================================================*/
-/**
- * @name Subsystem options
- * @{
- */
-/*===========================================================================*/
-
-/**
- * @brief Threads registry APIs.
- * @details If enabled then the registry APIs are included in the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__)
-#define CH_USE_REGISTRY TRUE
-#endif
-
-/**
- * @brief Threads synchronization APIs.
- * @details If enabled then the @p chThdWait() function is included in
- * the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__)
-#define CH_USE_WAITEXIT TRUE
-#endif
-
-/**
- * @brief Semaphores APIs.
- * @details If enabled then the Semaphores APIs are included in the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__)
-#define CH_USE_SEMAPHORES TRUE
-#endif
-
-/**
- * @brief Semaphores queuing mode.
- * @details If enabled then the threads are enqueued on semaphores by
- * priority rather than in FIFO order.
- *
- * @note The default is @p FALSE. Enable this if you have special requirements.
- * @note Requires @p CH_USE_SEMAPHORES.
- */
-#if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__)
-#define CH_USE_SEMAPHORES_PRIORITY FALSE
-#endif
-
-/**
- * @brief Atomic semaphore API.
- * @details If enabled then the semaphores the @p chSemSignalWait() API
- * is included in the kernel.
- *
- * @note The default is @p TRUE.
- * @note Requires @p CH_USE_SEMAPHORES.
- */
-#if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__)
-#define CH_USE_SEMSW TRUE
-#endif
-
-/**
- * @brief Mutexes APIs.
- * @details If enabled then the mutexes APIs are included in the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__)
-#define CH_USE_MUTEXES TRUE
-#endif
-
-/**
- * @brief Conditional Variables APIs.
- * @details If enabled then the conditional variables APIs are included
- * in the kernel.
- *
- * @note The default is @p TRUE.
- * @note Requires @p CH_USE_MUTEXES.
- */
-#if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__)
-#define CH_USE_CONDVARS TRUE
-#endif
-
-/**
- * @brief Conditional Variables APIs with timeout.
- * @details If enabled then the conditional variables APIs with timeout
- * specification are included in the kernel.
- *
- * @note The default is @p TRUE.
- * @note Requires @p CH_USE_CONDVARS.
- */
-#if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__)
-#define CH_USE_CONDVARS_TIMEOUT TRUE
-#endif
-
-/**
- * @brief Events Flags APIs.
- * @details If enabled then the event flags APIs are included in the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__)
-#define CH_USE_EVENTS TRUE
-#endif
-
-/**
- * @brief Events Flags APIs with timeout.
- * @details If enabled then the events APIs with timeout specification
- * are included in the kernel.
- *
- * @note The default is @p TRUE.
- * @note Requires @p CH_USE_EVENTS.
- */
-#if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__)
-#define CH_USE_EVENTS_TIMEOUT TRUE
-#endif
-
-/**
- * @brief Synchronous Messages APIs.
- * @details If enabled then the synchronous messages APIs are included
- * in the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__)
-#define CH_USE_MESSAGES TRUE
-#endif
-
-/**
- * @brief Synchronous Messages queuing mode.
- * @details If enabled then messages are served by priority rather than in
- * FIFO order.
- *
- * @note The default is @p FALSE. Enable this if you have special requirements.
- * @note Requires @p CH_USE_MESSAGES.
- */
-#if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__)
-#define CH_USE_MESSAGES_PRIORITY FALSE
-#endif
-
-/**
- * @brief Mailboxes APIs.
- * @details If enabled then the asynchronous messages (mailboxes) APIs are
- * included in the kernel.
- *
- * @note The default is @p TRUE.
- * @note Requires @p CH_USE_SEMAPHORES.
- */
-#if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__)
-#define CH_USE_MAILBOXES TRUE
-#endif
-
-/**
- * @brief I/O Queues APIs.
- * @details If enabled then the I/O queues APIs are included in the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__)
-#define CH_USE_QUEUES TRUE
-#endif
-
-/**
- * @brief Core Memory Manager APIs.
- * @details If enabled then the core memory manager APIs are included
- * in the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__)
-#define CH_USE_MEMCORE TRUE
-#endif
-
-/**
- * @brief Heap Allocator APIs.
- * @details If enabled then the memory heap allocator APIs are included
- * in the kernel.
- *
- * @note The default is @p TRUE.
- * @note Requires @p CH_USE_MEMCORE and either @p CH_USE_MUTEXES or
- * @p CH_USE_SEMAPHORES.
- * @note Mutexes are recommended.
- */
-#if !defined(CH_USE_HEAP) || defined(__DOXYGEN__)
-#define CH_USE_HEAP TRUE
-#endif
-
-/**
- * @brief C-runtime allocator.
- * @details If enabled the the heap allocator APIs just wrap the C-runtime
- * @p malloc() and @p free() functions.
- *
- * @note The default is @p FALSE.
- * @note Requires @p CH_USE_HEAP.
- * @note The C-runtime may or may not require @p CH_USE_MEMCORE, see the
- * appropriate documentation.
- */
-#if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__)
-#define CH_USE_MALLOC_HEAP FALSE
-#endif
-
-/**
- * @brief Memory Pools Allocator APIs.
- * @details If enabled then the memory pools allocator APIs are included
- * in the kernel.
- *
- * @note The default is @p TRUE.
- */
-#if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__)
-#define CH_USE_MEMPOOLS TRUE
-#endif
-
-/**
- * @brief Dynamic Threads APIs.
- * @details If enabled then the dynamic threads creation APIs are included
- * in the kernel.
- *
- * @note The default is @p TRUE.
- * @note Requires @p CH_USE_WAITEXIT.
- * @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS.
- */
-#if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__)
-#define CH_USE_DYNAMIC TRUE
-#endif
-
-/** @} */
-
-/*===========================================================================*/
-/**
- * @name Debug options
- * @{
- */
-/*===========================================================================*/
-
-/**
- * @brief Debug option, system state check.
- * @details If enabled the correct call protocol for system APIs is checked
- * at runtime.
- *
- * @note The default is @p FALSE.
- */
-#if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__)
-#define CH_DBG_SYSTEM_STATE_CHECK FALSE
-#endif
-
-/**
- * @brief Debug option, parameters checks.
- * @details If enabled then the checks on the API functions input
- * parameters are activated.
- *
- * @note The default is @p FALSE.
- */
-#if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__)
-#define CH_DBG_ENABLE_CHECKS FALSE
-#endif
-
-/**
- * @brief Debug option, consistency checks.
- * @details If enabled then all the assertions in the kernel code are
- * activated. This includes consistency checks inside the kernel,
- * runtime anomalies and port-defined checks.
- *
- * @note The default is @p FALSE.
- */
-#if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__)
-#define CH_DBG_ENABLE_ASSERTS FALSE
-#endif
-
-/**
- * @brief Debug option, trace buffer.
- * @details If enabled then the context switch circular trace buffer is
- * activated.
- *
- * @note The default is @p FALSE.
- */
-#if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__)
-#define CH_DBG_ENABLE_TRACE FALSE
-#endif
-
-/**
- * @brief Debug option, stack checks.
- * @details If enabled then a runtime stack check is performed.
- *
- * @note The default is @p FALSE.
- * @note The stack check is performed in a architecture/port dependent way.
- * It may not be implemented or some ports.
- * @note The default failure mode is to halt the system with the global
- * @p panic_msg variable set to @p NULL.
- */
-#if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__)
-#define CH_DBG_ENABLE_STACK_CHECK FALSE
-#endif
-
-/**
- * @brief Debug option, stacks initialization.
- * @details If enabled then the threads working area is filled with a byte
- * value when a thread is created. This can be useful for the
- * runtime measurement of the used stack.
- *
- * @note The default is @p FALSE.
- */
-#if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__)
-#define CH_DBG_FILL_THREADS FALSE
-#endif
-
-/**
- * @brief Debug option, threads profiling.
- * @details If enabled then a field is added to the @p Thread structure that
- * counts the system ticks occurred while executing the thread.
- *
- * @note The default is @p TRUE.
- * @note This debug option is defaulted to TRUE because it is required by
- * some test cases into the test suite.
- */
-#if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__)
-#define CH_DBG_THREADS_PROFILING TRUE
-#endif
-
-/** @} */
-
-/*===========================================================================*/
-/**
- * @name Kernel hooks
- * @{
- */
-/*===========================================================================*/
-
-/**
- * @brief Threads descriptor structure extension.
- * @details User fields added to the end of the @p Thread structure.
- */
-#if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__)
-#define THREAD_EXT_FIELDS \
- /* Add threads custom fields here.*/
-#endif
-
-/**
- * @brief Threads initialization hook.
- * @details User initialization code added to the @p chThdInit() API.
- *
- * @note It is invoked from within @p chThdInit() and implicitly from all
- * the threads creation APIs.
- */
-#if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__)
-#define THREAD_EXT_INIT_HOOK(tp) { \
- /* Add threads initialization code here.*/ \
-}
-#endif
-
-/**
- * @brief Threads finalization hook.
- * @details User finalization code added to the @p chThdExit() API.
- *
- * @note It is inserted into lock zone.
- * @note It is also invoked when the threads simply return in order to
- * terminate.
- */
-#if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__)
-#define THREAD_EXT_EXIT_HOOK(tp) { \
- /* Add threads finalization code here.*/ \
-}
-#endif
-
-/**
- * @brief Context switch hook.
- * @details This hook is invoked just before switching between threads.
- */
-#if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__)
-#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \
- /* System halt code here.*/ \
-}
-#endif
-
-/**
- * @brief Idle Loop hook.
- * @details This hook is continuously invoked by the idle thread loop.
- */
-#if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__)
-#define IDLE_LOOP_HOOK() { \
- /* Idle loop code here.*/ \
-}
-#endif
-
-/**
- * @brief System tick event hook.
- * @details This hook is invoked in the system tick handler immediately
- * after processing the virtual timers queue.
- */
-#if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__)
-#define SYSTEM_TICK_EVENT_HOOK() { \
- /* System tick event code here.*/ \
-}
-#endif
-
-/**
- * @brief System halt hook.
- * @details This hook is invoked in case to a system halting error before
- * the system is halted.
- */
-#if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__)
-#define SYSTEM_HALT_HOOK() { \
- /* System halt code here.*/ \
-}
-#endif
-
-/** @} */
-
-/*===========================================================================*/
-/* Port-specific settings (override port settings defaulted in chcore.h). */
-/*===========================================================================*/
-
-#endif /* _CHCONF_H_ */
-
-/** @} */
diff --git a/halconf.h b/halconf.h deleted file mode 100644 index aaf2f31..0000000 --- a/halconf.h +++ /dev/null @@ -1,345 +0,0 @@ -/*
- ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
-
- 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.
-*/
-
-/**
- * @file templates/halconf.h
- * @brief HAL configuration header.
- * @details HAL configuration file, this file allows to enable or disable the
- * various device drivers from your application. You may also use
- * this file in order to override the device drivers default settings.
- *
- * @addtogroup HAL_CONF
- * @{
- */
-
-#ifndef _HALCONF_H_
-#define _HALCONF_H_
-
-#include "mcuconf.h"
-
-/**
- * @brief Enables the TM subsystem.
- */
-#if !defined(HAL_USE_TM) || defined(__DOXYGEN__)
-#define HAL_USE_TM TRUE
-#endif
-
-/**
- * @brief Enables the PAL subsystem.
- */
-#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
-#define HAL_USE_PAL TRUE
-#endif
-
-/**
- * @brief Enables the ADC subsystem.
- */
-#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
-#define HAL_USE_ADC TRUE
-#endif
-
-/**
- * @brief Enables the CAN subsystem.
- */
-#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
-#define HAL_USE_CAN FALSE
-#endif
-
-/**
- * @brief Enables the EXT subsystem.
- */
-#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__)
-#define HAL_USE_EXT TRUE
-#endif
-
-/**
- * @brief Enables the GPT subsystem.
- */
-#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
-#define HAL_USE_GPT TRUE
-#endif
-
-/**
- * @brief Enables the I2C subsystem.
- */
-#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
-#define HAL_USE_I2C TRUE
-#endif
-
-/**
- * @brief Enables the ICU subsystem.
- */
-#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
-#define HAL_USE_ICU FALSE
-#endif
-
-/**
- * @brief Enables the MAC subsystem.
- */
-#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
-#define HAL_USE_MAC FALSE
-#endif
-
-/**
- * @brief Enables the MMC_SPI subsystem.
- */
-#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
-#define HAL_USE_MMC_SPI FALSE
-#endif
-
-/**
- * @brief Enables the PWM subsystem.
- */
-#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
-#define HAL_USE_PWM FALSE
-#endif
-
-/**
- * @brief Enables the RTC subsystem.
- */
-#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
-#define HAL_USE_RTC FALSE
-#endif
-
-/**
- * @brief Enables the SDC subsystem.
- */
-#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
-#define HAL_USE_SDC FALSE
-#endif
-
-/**
- * @brief Enables the SERIAL subsystem.
- */
-#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL TRUE
-#endif
-
-/**
- * @brief Enables the SERIAL over USB subsystem.
- */
-#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
-#define HAL_USE_SERIAL_USB FALSE
-#endif
-
-/**
- * @brief Enables the SPI subsystem.
- */
-#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
-#define HAL_USE_SPI TRUE
-#endif
-
-/**
- * @brief Enables the UART subsystem.
- */
-#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
-#define HAL_USE_UART FALSE
-#endif
-
-/**
- * @brief Enables the USB subsystem.
- */
-#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
-#define HAL_USE_USB FALSE
-#endif
-
-/*===========================================================================*/
-/* ADC driver related settings. */
-/*===========================================================================*/
-
-/**
- * @brief Enables synchronous APIs.
- * @note Disabling this option saves both code and data space.
- */
-#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
-#define ADC_USE_WAIT TRUE
-#endif
-
-/**
- * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
- * @note Disabling this option saves both code and data space.
- */
-#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
-#define ADC_USE_MUTUAL_EXCLUSION TRUE
-#endif
-
-/*===========================================================================*/
-/* CAN driver related settings. */
-/*===========================================================================*/
-
-/**
- * @brief Sleep mode related APIs inclusion switch.
- */
-#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
-#define CAN_USE_SLEEP_MODE TRUE
-#endif
-
-/*===========================================================================*/
-/* I2C driver related settings. */
-/*===========================================================================*/
-
-/**
- * @brief Enables the mutual exclusion APIs on the I2C bus.
- */
-#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
-#define I2C_USE_MUTUAL_EXCLUSION TRUE
-#endif
-
-/*===========================================================================*/
-/* MAC driver related settings. */
-/*===========================================================================*/
-
-/**
- * @brief Enables an event sources for incoming packets.
- */
-#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
-#define MAC_USE_ZERO_COPY FALSE
-#endif
-
-/**
- * @brief Enables an event sources for incoming packets.
- */
-#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
-#define MAC_USE_EVENTS TRUE
-#endif
-
-/*===========================================================================*/
-/* MMC_SPI driver related settings. */
-/*===========================================================================*/
-
-/**
- * @brief Block size for MMC transfers.
- */
-#if !defined(MMC_SECTOR_SIZE) || defined(__DOXYGEN__)
-#define MMC_SECTOR_SIZE 512
-#endif
-
-/**
- * @brief Delays insertions.
- * @details If enabled this options inserts delays into the MMC waiting
- * routines releasing some extra CPU time for the threads with
- * lower priority, this may slow down the driver a bit however.
- * This option is recommended also if the SPI driver does not
- * use a DMA channel and heavily loads the CPU.
- */
-#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__)
-#define MMC_NICE_WAITING TRUE
-#endif
-
-/**
- * @brief Number of positive insertion queries before generating the
- * insertion event.
- */
-#if !defined(MMC_POLLING_INTERVAL) || defined(__DOXYGEN__)
-#define MMC_POLLING_INTERVAL 10
-#endif
-
-/**
- * @brief Interval, in milliseconds, between insertion queries.
- */
-#if !defined(MMC_POLLING_DELAY) || defined(__DOXYGEN__)
-#define MMC_POLLING_DELAY 10
-#endif
-
-/**
- * @brief Uses the SPI polled API for small data transfers.
- * @details Polled transfers usually improve performance because it
- * saves two context switches and interrupt servicing. Note
- * that this option has no effect on large transfers which
- * are always performed using DMAs/IRQs.
- */
-#if !defined(MMC_USE_SPI_POLLING) || defined(__DOXYGEN__)
-#define MMC_USE_SPI_POLLING TRUE
-#endif
-
-/*===========================================================================*/
-/* SDC driver related settings. */
-/*===========================================================================*/
-
-/**
- * @brief Number of initialization attempts before rejecting the card.
- * @note Attempts are performed at 10mS intervals.
- */
-#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
-#define SDC_INIT_RETRY 100
-#endif
-
-/**
- * @brief Include support for MMC cards.
- * @note MMC support is not yet implemented so this option must be kept
- * at @p FALSE.
- */
-#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
-#define SDC_MMC_SUPPORT FALSE
-#endif
-
-/**
- * @brief Delays insertions.
- * @details If enabled this options inserts delays into the MMC waiting
- * routines releasing some extra CPU time for the threads with
- * lower priority, this may slow down the driver a bit however.
- */
-#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
-#define SDC_NICE_WAITING TRUE
-#endif
-
-/*===========================================================================*/
-/* SERIAL driver related settings. */
-/*===========================================================================*/
-
-/**
- * @brief Default bit rate.
- * @details Configuration parameter, this is the baud rate selected for the
- * default configuration.
- */
-#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
-#define SERIAL_DEFAULT_BITRATE 19200
-#endif
-
-/**
- * @brief Serial buffers size.
- * @details Configuration parameter, you can change the depth of the queue
- * buffers depending on the requirements of your application.
- * @note The default is 64 bytes for both the transmission and receive
- * buffers.
- */
-#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
-#define SERIAL_BUFFERS_SIZE 64
-#endif
-
-/*===========================================================================*/
-/* SPI driver related settings. */
-/*===========================================================================*/
-
-/**
- * @brief Enables synchronous APIs.
- * @note Disabling this option saves both code and data space.
- */
-#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
-#define SPI_USE_WAIT TRUE
-#endif
-
-/**
- * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
- * @note Disabling this option saves both code and data space.
- */
-#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
-#define SPI_USE_MUTUAL_EXCLUSION TRUE
-#endif
-
-#endif /* _HALCONF_H_ */
-
-/** @} */
diff --git a/include/control.h b/include/control.h deleted file mode 100644 index ee9cca2..0000000 --- a/include/control.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef CONTROL_H_ -#define CONTROL_H_ - -#include <stdint.h> - -#include "ch.h" -#include "hal.h" -#include "vex.h" - -typedef enum { - BUTTON_UP, - BUTTON_RELEASE, - BUTTON_DOWN -} button_t; - -struct joydata_t { - int Ch1 :8; - int Ch2 :8; - int Ch3 :8; - int Ch4 :8; - - struct accel_t { - int y :8; - int x :8; - int z :8; - } __attribute__ ((packed)) accel; - - char Btn5D :1; - char Btn5U :1; - char Btn6D :1; - char Btn6U :1; - - char Reserved :4; - - char Btn8D :1; - char Btn8L :1; - char Btn8U :1; - char Btn8R :1; - char Btn7D :1; - char Btn7L :1; - char Btn7U :1; - char Btn7R :1; - - char Reserved2[2]; - -} __attribute__ ((packed)); - -struct Controller { -private: - int index; - struct joydata_t* data; - -public: - Controller(int idx = 1) - : index(idx), data(nullptr) {} - ~Controller(void) {} - - void update(void) { - data = reinterpret_cast<struct joydata_t*>(vexSpiGetJoystickDataPtr(index)); - data->Ch1 = vexControllerGet(Ch1);//( data->Ch1 == 0xFF ) ? 127 : data->Ch1 - 127; - data->Ch2 = vexControllerGet(Ch2);//-(( data->Ch2 == 0xFF ) ? 127 : data->Ch2 - 127); - data->Ch3 = vexControllerGet(Ch3);//-(( data->Ch3 == 0xFF ) ? 127 : data->Ch3 - 127); - data->Ch4 = vexControllerGet(Ch4);//( data->Ch4 == 0xFF ) ? 127 : data->Ch4 - 127; - } - - inline const struct joydata_t* operator->(void) const - { return data; } - -} __attribute__ ((packed)); - -#endif // CONTROL_H_ diff --git a/include/distortos/ConditionVariable.hpp b/include/distortos/ConditionVariable.hpp new file mode 100644 index 0000000..6b7f869 --- /dev/null +++ b/include/distortos/ConditionVariable.hpp @@ -0,0 +1,304 @@ +/** + * \file + * \brief ConditionVariable class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_ +#define INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_ + +#include "distortos/internal/scheduler/ThreadList.hpp" + +#include "distortos/TickClock.hpp" + +namespace distortos +{ + +class Mutex; + +/** + * \brief ConditionVariable is an advanced synchronization primitive + * + * Similar to std::condition_variable - http://en.cppreference.com/w/cpp/thread/condition_variable + * Similar to POSIX pthread_cond_t + * + * \ingroup synchronization + */ + +class ConditionVariable +{ +public: + + /** + * \brief ConditionVariable constructor + * + * Similar to std::condition_variable::condition_variable() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/condition_variable + * Similar to pthread_cond_init() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_init.html + */ + + constexpr ConditionVariable() : + blockedList_{} + { + + } + + /** + * \brief Notifies all waiting threads. + * + * Similar to std::condition_variable::notify_all() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all + * Similar to pthread_cond_broadcast() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html + * + * Unblocks all threads waiting on this condition variable. The notifying thread does not need to hold the same + * mutex as the one held by the waiting thread(s). + */ + + void notifyAll(); + + /** + * \brief Notifies one waiting thread. + * + * Similar to std::condition_variable::notify_one() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/notify_one + * Similar to pthread_cond_signal() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html + * + * Unblocks one thread waiting on this condition variable. The notifying thread does not need to hold the same + * mutex as the one held by the waiting thread(s). + */ + + void notifyOne(); + + /** + * \brief Waits for notification. + * + * Similar to std::condition_variable::wait() - http://en.cppreference.com/w/cpp/thread/condition_variable/wait + * Similar to pthread_cond_wait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html + * + * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread + * will be unblocked when notifyAll() or notifyOne() is executed. It may also be unblocked spuriously. When + * unblocked, regardless of the reason, lock is reacquired and wait exits. + * + * \param [in] mutex is a reference to mutex which must be owned by calling thread + * + * \return zero if the wait was completed successfully, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + */ + + int wait(Mutex& mutex); + + /** + * \brief Waits for predicate to become true. + * + * Similar to std::condition_variable::wait() - http://en.cppreference.com/w/cpp/thread/condition_variable/wait + * Similar to pthread_cond_wait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_wait.html + * + * Overload for wait() which also checks the predicate. This function will return only if the predicate is true. + * + * \tparam Predicate is a type of functor to check the predicate + * + * \param [in] mutex is a reference to mutex which must be owned by calling thread + * \param [in] predicate is the predicate that will be checked + * + * \return zero if the wait was completed successfully, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + */ + + template<typename Predicate> + int wait(Mutex& mutex, Predicate predicate); + + /** + * \brief Waits for notification for given duration of time. + * + * Similar to std::condition_variable::wait_for() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for + * Similar to pthread_cond_timedwait() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html# + * + * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread + * will be unblocked when notifyAll() or notifyOne() is executed or when given duration of time expires. It may also + * be unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait exits. + * + * \param [in] mutex is a reference to mutex which must be owned by calling thread + * \param [in] duration is the duration after which the wait for notification will be terminated + * + * \return zero if the wait was completed successfully, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + * - ETIMEDOUT - no notification was received before the specified timeout expired; + */ + + int waitFor(Mutex& mutex, TickClock::duration duration); + + /** + * \brief Waits for notification for given duration of time. + * + * Similar to std::condition_variable::wait_for() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for + * Similar to pthread_cond_timedwait() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html# + * + * Template variant of waitFor(Mutex& mutex, TickClock::duration duration). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] mutex is a reference to mutex which must be owned by calling thread + * \param [in] duration is the duration after which the wait for notification will be terminated + * + * \return zero if the wait was completed successfully, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + * - ETIMEDOUT - no notification was received before the specified timeout expired; + */ + + template<typename Rep, typename Period> + int waitFor(Mutex& mutex, const std::chrono::duration<Rep, Period> duration) + { + return waitFor(mutex, std::chrono::duration_cast<TickClock::duration>(duration)); + } + + /** + * \brief Waits for predicate to become true for given duration of time. + * + * Similar to std::condition_variable::wait_for() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for + * Similar to pthread_cond_timedwait() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html# + * + * Overload for waitFor() which also checks the predicate. This function will return only if the predicate is true + * or when given duration of time expires. + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * \tparam Predicate is a type of functor to check the predicate + * + * \param [in] mutex is a reference to mutex which must be owned by calling thread + * \param [in] duration is the duration after which the wait for notification will be terminated + * \param [in] predicate is the predicate that will be checked + * + * \return zero if the wait was completed successfully, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + * - ETIMEDOUT - no notification was received before the specified timeout expired; + */ + + template<typename Rep, typename Period, typename Predicate> + int waitFor(Mutex& mutex, const std::chrono::duration<Rep, Period> duration, Predicate predicate) + { + return waitUntil(mutex, TickClock::now() + duration + TickClock::duration{1}, std::move(predicate)); + } + + /** + * \brief Waits for notification until given time point. + * + * Similar to std::condition_variable::wait_until() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until + * Similar to pthread_cond_timedwait() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html# + * + * Atomically releases supplied mutex and blocks current thread until the condition variable is notified. The thread + * will be unblocked when notifyAll() or notifyOne() is executed or when given time point is reached. It may also be + * unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait exits. + * + * \param [in] mutex is a reference to mutex which must be owned by calling thread + * \param [in] timePoint is the time point at which the wait for notification will be terminated + * + * \return zero if the wait was completed successfully, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + * - ETIMEDOUT - no notification was received before the specified timeout expired; + */ + + int waitUntil(Mutex& mutex, TickClock::time_point timePoint); + + /** + * \brief Waits for notification until given time point. + * + * Similar to std::condition_variable::wait_until() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until + * Similar to pthread_cond_timedwait() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html# + * + * Template variant of waitUntil(Mutex& mutex, TickClock::time_point timePoint). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] mutex is a reference to mutex which must be owned by calling thread + * \param [in] timePoint is the time point at which the wait for notification will be terminated + * + * \return zero if the wait was completed successfully, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + * - ETIMEDOUT - no notification was received before the specified timeout expired; + */ + + template<typename Duration> + int waitUntil(Mutex& mutex, const std::chrono::time_point<TickClock, Duration> timePoint) + { + return waitUntil(mutex, std::chrono::time_point_cast<TickClock::duration>(timePoint)); + } + + /** + * \brief Waits for predicate to become true until given time point. + * + * Similar to std::condition_variable::wait_until() - + * http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until + * Similar to pthread_cond_timedwait() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html# + * + * Overload for waitUntil() which also checks the predicate. This function will return only if the predicate is true + * or when given time point is reached. + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * \tparam Predicate is a type of functor to check the predicate + * + * \param [in] mutex is a reference to mutex which must be owned by calling thread + * \param [in] timePoint is the time point at which the wait for notification will be terminated + * \param [in] predicate is the predicate that will be checked + * + * \return zero if the wait was completed successfully, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + * - ETIMEDOUT - no notification was received before the specified timeout expired; + */ + + template<typename Duration, typename Predicate> + int waitUntil(Mutex& mutex, std::chrono::time_point<TickClock, Duration> timePoint, Predicate predicate); + +private: + + /// ThreadControlBlock objects blocked on this condition variable + internal::ThreadList blockedList_; +}; + +template<typename Predicate> +int ConditionVariable::wait(Mutex& mutex, Predicate predicate) +{ + while (predicate() == false) + { + const auto ret = wait(mutex); + if (ret != 0) + return ret; + } + + return 0; +} + +template<typename Duration, typename Predicate> +int ConditionVariable::waitUntil(Mutex& mutex, const std::chrono::time_point<TickClock, Duration> timePoint, Predicate predicate) +{ + while (predicate() == false) + { + const auto ret = waitUntil(mutex, timePoint); + if (ret != 0) + return ret; + } + + return 0; +} + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_CONDITIONVARIABLE_HPP_ diff --git a/include/distortos/DynamicFifoQueue.hpp b/include/distortos/DynamicFifoQueue.hpp new file mode 100644 index 0000000..fba876f --- /dev/null +++ b/include/distortos/DynamicFifoQueue.hpp @@ -0,0 +1,56 @@ +/** + * \file + * \brief DynamicFifoQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DYNAMICFIFOQUEUE_HPP_ +#define INCLUDE_DISTORTOS_DYNAMICFIFOQUEUE_HPP_ + +#include "FifoQueue.hpp" + +#include "distortos/internal/memory/storageDeleter.hpp" + +namespace distortos +{ + +/** + * \brief DynamicFifoQueue class is a variant of FifoQueue that has dynamic storage for queue's contents. + * + * \tparam T is the type of data in queue + * + * \ingroup queues + */ + +template<typename T> +class DynamicFifoQueue : public FifoQueue<T> +{ +public: + + /// import Storage type from base class + using typename FifoQueue<T>::Storage; + + /** + * \brief DynamicFifoQueue's constructor + * + * \param [in] queueSize is the maximum number of elements in queue + */ + + explicit DynamicFifoQueue(size_t queueSize); +}; + +template<typename T> +DynamicFifoQueue<T>::DynamicFifoQueue(const size_t queueSize) : + FifoQueue<T>{{new Storage[queueSize], internal::storageDeleter<Storage>}, queueSize} +{ + +} + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_DYNAMICFIFOQUEUE_HPP_ diff --git a/include/distortos/DynamicMessageQueue.hpp b/include/distortos/DynamicMessageQueue.hpp new file mode 100644 index 0000000..b298351 --- /dev/null +++ b/include/distortos/DynamicMessageQueue.hpp @@ -0,0 +1,60 @@ +/** + * \file + * \brief DynamicMessageQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DYNAMICMESSAGEQUEUE_HPP_ +#define INCLUDE_DISTORTOS_DYNAMICMESSAGEQUEUE_HPP_ + +#include "MessageQueue.hpp" + +#include "distortos/internal/memory/storageDeleter.hpp" + +namespace distortos +{ + +/** + * \brief DynamicMessageQueue class is a variant of MessageQueue that has dynamic storage for queue's contents. + * + * \tparam T is the type of data in queue + * + * \ingroup queues + */ + +template<typename T> +class DynamicMessageQueue : public MessageQueue<T> +{ +public: + + /// import EntryStorage type from base class + using typename MessageQueue<T>::EntryStorage; + + /// import ValueStorage type from base class + using typename MessageQueue<T>::ValueStorage; + + /** + * \brief DynamicMessageQueue's constructor + * + * \param [in] queueSize is the maximum number of elements in queue + */ + + explicit DynamicMessageQueue(size_t queueSize); +}; + +template<typename T> +DynamicMessageQueue<T>::DynamicMessageQueue(const size_t queueSize) : + MessageQueue<T>{{new EntryStorage[queueSize], internal::storageDeleter<EntryStorage>}, + {new ValueStorage[queueSize], internal::storageDeleter<ValueStorage>}, queueSize} +{ + +} + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_DYNAMICMESSAGEQUEUE_HPP_ diff --git a/include/distortos/DynamicRawFifoQueue.hpp b/include/distortos/DynamicRawFifoQueue.hpp new file mode 100644 index 0000000..afd2f0b --- /dev/null +++ b/include/distortos/DynamicRawFifoQueue.hpp @@ -0,0 +1,42 @@ +/** + * \file + * \brief DynamicRawFifoQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DYNAMICRAWFIFOQUEUE_HPP_ +#define INCLUDE_DISTORTOS_DYNAMICRAWFIFOQUEUE_HPP_ + +#include "RawFifoQueue.hpp" + +namespace distortos +{ + +/** + * \brief DynamicRawFifoQueue class is a variant of RawFifoQueue that has dynamic storage for queue's contents. + * + * \ingroup queues + */ + +class DynamicRawFifoQueue : public RawFifoQueue +{ +public: + + /** + * \brief DynamicRawFifoQueue's constructor + * + * \param [in] elementSize is the size of single queue element, bytes + * \param [in] queueSize is the maximum number of elements in queue + */ + + DynamicRawFifoQueue(size_t elementSize, size_t queueSize); +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_DYNAMICRAWFIFOQUEUE_HPP_ diff --git a/include/distortos/DynamicRawMessageQueue.hpp b/include/distortos/DynamicRawMessageQueue.hpp new file mode 100644 index 0000000..ab68807 --- /dev/null +++ b/include/distortos/DynamicRawMessageQueue.hpp @@ -0,0 +1,42 @@ +/** + * \file + * \brief DynamicRawMessageQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DYNAMICRAWMESSAGEQUEUE_HPP_ +#define INCLUDE_DISTORTOS_DYNAMICRAWMESSAGEQUEUE_HPP_ + +#include "distortos/RawMessageQueue.hpp" + +namespace distortos +{ + +/** + * \brief DynamicRawMessageQueue class is a variant of RawMessageQueue that has dynamic storage for queue's contents. + * + * \ingroup queues + */ + +class DynamicRawMessageQueue : public RawMessageQueue +{ +public: + + /** + * \brief DynamicRawMessageQueue's constructor + * + * \param [in] elementSize is the size of single queue element, bytes + * \param [in] queueSize is the maximum number of elements in queue + */ + + DynamicRawMessageQueue(size_t elementSize, size_t queueSize); +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_DYNAMICRAWMESSAGEQUEUE_HPP_ diff --git a/include/distortos/DynamicSignalsReceiver.hpp b/include/distortos/DynamicSignalsReceiver.hpp new file mode 100644 index 0000000..386a612 --- /dev/null +++ b/include/distortos/DynamicSignalsReceiver.hpp @@ -0,0 +1,52 @@ +/** + * \file + * \brief DynamicSignalsReceiver class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DYNAMICSIGNALSRECEIVER_HPP_ +#define INCLUDE_DISTORTOS_DYNAMICSIGNALSRECEIVER_HPP_ + +#include "distortos/SignalInformationQueueWrapper.hpp" +#include "distortos/SignalsCatcher.hpp" +#include "distortos/SignalsReceiver.hpp" + +namespace distortos +{ + +/** + * \brief DynamicSignalsReceiver class is a templated interface for SignalsReceiver that has dynamic storage for queued + * signals and SignalAction associations required for catching signals. + */ + +class DynamicSignalsReceiver : public SignalsReceiver +{ +public: + + /** + * \brief DynamicSignalsReceiver's constructor + * + * \param [in] queuedSignals is the max number of queued signals, 0 to disable queuing of signals for this receiver + * \param [in] signalActions is the max number of different SignalAction objects, 0 to disable catching of signals + * for this receiver + */ + + DynamicSignalsReceiver(size_t queuedSignals, size_t signalActions); + +private: + + /// internal SignalInformationQueueWrapper object + SignalInformationQueueWrapper signalInformationQueueWrapper_; + + /// internal SignalsCatcher object + SignalsCatcher signalsCatcher_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_DYNAMICSIGNALSRECEIVER_HPP_ diff --git a/include/distortos/DynamicThread.hpp b/include/distortos/DynamicThread.hpp new file mode 100644 index 0000000..4d7550a --- /dev/null +++ b/include/distortos/DynamicThread.hpp @@ -0,0 +1,373 @@ +/** + * \file + * \brief DynamicThread class header + * + * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DYNAMICTHREAD_HPP_ +#define INCLUDE_DISTORTOS_DYNAMICTHREAD_HPP_ + +#include "distortos/internal/scheduler/DynamicThreadBase.hpp" + +namespace distortos +{ + +/// \addtogroup threads +/// \{ + +/** + * \brief DynamicThread class is a type-erased interface for thread that has dynamic storage for bounded function, stack + * and internal DynamicSignalsReceiver object. + */ + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +class DynamicThread : public Thread +{ +public: + + /** + * \brief DynamicThread's constructor + * + * \tparam Function is the function that will be executed in separate thread + * \tparam Args are the arguments for \a Function + * + * \param [in] stackSize is the size of stack, bytes + * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this + * thread + * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread + * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable catching of signals for this thread + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + */ + + template<typename Function, typename... Args> + DynamicThread(size_t stackSize, bool canReceiveSignals, size_t queuedSignals, size_t signalActions, + uint8_t priority, SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args); + + /** + * \brief DynamicThread's constructor + * + * \tparam Function is the function that will be executed in separate thread + * \tparam Args are the arguments for \a Function + * + * \param [in] parameters is a DynamicThreadParameters struct with thread parameters + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + */ + + template<typename Function, typename... Args> + DynamicThread(const DynamicThreadParameters parameters, Function&& function, Args&&... args) : + DynamicThread{parameters.stackSize, parameters.canReceiveSignals, parameters.queuedSignals, + parameters.signalActions, parameters.priority, parameters.schedulingPolicy, + std::forward<Function>(function), std::forward<Args>(args)...} + { + + } + + /** + * \brief DynamicThread's destructor + */ + + ~DynamicThread() override; + + /** + * \brief Detaches the thread. + * + * Similar to std::thread::detach() - http://en.cppreference.com/w/cpp/thread/thread/detach + * Similar to POSIX pthread_detach() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html + * + * Detaches the executing thread from the Thread object, allowing execution to continue independently. All resources + * allocated for the thread will be deallocated when the thread terminates. + * + * \return 0 on success, error code otherwise: + * - EINVAL - this thread is already detached; + */ + + int detach() override; + + /** + * \brief Generates signal for thread. + * + * Similar to pthread_kill() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html + * + * Adds the signalNumber to set of pending signals. If this thread is currently waiting for this signal, it will be + * unblocked. + * + * \param [in] signalNumber is the signal that will be generated, [0; 31] + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception of signals is disabled for this thread; + * - EINVAL - internal thread object was detached; + * + * \ingroup signals + */ + + int generateSignal(uint8_t signalNumber) override; + + /** + * \return effective priority of thread + */ + + uint8_t getEffectivePriority() const override; + + /** + * \brief Gets set of currently pending signals. + * + * Similar to sigpending() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigpending.html + * + * This function shall return the set of signals that are blocked from delivery and are pending on the thread. + * + * \return set of currently pending signals + * + * \ingroup signals + */ + + SignalSet getPendingSignalSet() const override; + + /** + * \return priority of thread + */ + + uint8_t getPriority() const override; + + /** + * \return scheduling policy of the thread + */ + + SchedulingPolicy getSchedulingPolicy() const override; + + /** + * \return current state of thread + */ + + ThreadState getState() const override; + + /** + * \brief Waits for thread termination. + * + * Similar to std::thread::join() - http://en.cppreference.com/w/cpp/thread/thread/join + * Similar to POSIX pthread_join() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_join.html + * + * Blocks current thread until this thread finishes its execution. The results of multiple simultaneous calls to + * join() on the same target thread are undefined. + * + * \return 0 on success, error code otherwise: + * - EDEADLK - deadlock condition was detected, + * - EINVAL - this thread is not joinable, + * - EINVAL - internal thread object was detached; + * - ... + * + * \ingroup synchronization + */ + + int join() override; + + /** + * \brief Queues signal for thread. + * + * Similar to sigqueue() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigqueue.html + * + * Adds the signalNumber and signal value (sigval union) to queue of SignalInformation objects. If this thread is + * currently waiting for this signal, it will be unblocked. + * + * \param [in] signalNumber is the signal that will be queued, [0; 31] + * \param [in] value is the signal value + * + * \return 0 on success, error code otherwise: + * - EAGAIN - no resources are available to queue the signal, maximal number of signals is already queued in + * associated queue of SignalInformation objects; + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception or queuing of signals are disabled for this thread; + * - EINVAL - internal thread object was detached; + * + * \ingroup signals + */ + + int queueSignal(uint8_t signalNumber, sigval value) override; + + /** + * \brief Changes priority of thread. + * + * If the priority really changes, the position in the thread list is adjusted and context switch may be requested. + * + * \param [in] priority is the new priority of thread + * \param [in] alwaysBehind selects the method of ordering when lowering the priority + * - false - the thread is moved to the head of the group of threads with the new priority (default), + * - true - the thread is moved to the tail of the group of threads with the new priority. + */ + + void setPriority(uint8_t priority, bool alwaysBehind = {}) override; + + /** + * param [in] schedulingPolicy is the new scheduling policy of the thread + */ + + void setSchedulingPolicy(SchedulingPolicy schedulingPolicy) override; + + /** + * \brief Starts the thread. + * + * This operation can be performed on threads in "New" state only. + * + * \return 0 on success, error code otherwise: + * - EINVAL - thread is already started; + * - EINVAL - internal thread object was detached; + * - error codes returned by scheduler::Scheduler::add(); + */ + + int start() override; + + DynamicThread(const DynamicThread&) = delete; + DynamicThread(DynamicThread&&) = default; + const DynamicThread& operator=(const DynamicThread&) = delete; + DynamicThread& operator=(DynamicThread&&) = delete; + +private: + + /// internal thread object + std::unique_ptr<internal::DynamicThreadBase> detachableThread_; +}; + +#else // !def CONFIG_THREAD_DETACH_ENABLE + +class DynamicThread : public internal::DynamicThreadBase +{ +public: + + using internal::DynamicThreadBase::DynamicThreadBase; +}; + +#endif // !def CONFIG_THREAD_DETACH_ENABLE + +/** + * \brief Helper factory function to make DynamicThread object + * + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] stackSize is the size of stack, bytes + * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this + * thread + * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread + * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable catching of signals for this thread + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + * + * \return DynamicThread object + */ + +template<typename Function, typename... Args> +DynamicThread makeDynamicThread(const size_t stackSize, const bool canReceiveSignals, const size_t queuedSignals, + const size_t signalActions, const uint8_t priority, const SchedulingPolicy schedulingPolicy, + Function&& function, Args&&... args) +{ + return {stackSize, canReceiveSignals, queuedSignals, signalActions, priority, schedulingPolicy, + std::forward<Function>(function), std::forward<Args>(args)...}; +} + +/** + * \brief Helper factory function to make DynamicThread object + * + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] parameters is a DynamicThreadParameters struct with thread parameters + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + * + * \return DynamicThread object + */ + +template<typename Function, typename... Args> +DynamicThread makeDynamicThread(const DynamicThreadParameters parameters, Function&& function, Args&&... args) +{ + return {parameters, std::forward<Function>(function), std::forward<Args>(args)...}; +} + +/** + * \brief Helper factory function to make and start DynamicThread object + * + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] stackSize is the size of stack, bytes + * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this + * thread + * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread + * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable catching of signals for this thread + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + * + * \return DynamicThread object + */ + +template<typename Function, typename... Args> +DynamicThread makeAndStartDynamicThread(const size_t stackSize, const bool canReceiveSignals, + const size_t queuedSignals, const size_t signalActions, const uint8_t priority, + const SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args) +{ + auto thread = makeDynamicThread(stackSize, canReceiveSignals, queuedSignals, signalActions, priority, + schedulingPolicy, std::forward<Function>(function), std::forward<Args>(args)...); + thread.start(); /// \todo make sure this never fails + return thread; +} + +/** + * \brief Helper factory function to make and start DynamicThread object + * + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] parameters is a DynamicThreadParameters struct with thread parameters + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + * + * \return DynamicThread object + */ + +template<typename Function, typename... Args> +DynamicThread makeAndStartDynamicThread(const DynamicThreadParameters parameters, Function&& function, Args&&... args) +{ + auto thread = makeDynamicThread(parameters, std::forward<Function>(function), std::forward<Args>(args)...); + thread.start(); /// \todo make sure this never fails + return thread; +} + +/// \} + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +template<typename Function, typename... Args> +DynamicThread::DynamicThread(const size_t stackSize, const bool canReceiveSignals, const size_t queuedSignals, + const size_t signalActions, const uint8_t priority, const SchedulingPolicy schedulingPolicy, + Function&& function, Args&&... args) : + detachableThread_{new internal::DynamicThreadBase{stackSize, canReceiveSignals, queuedSignals, signalActions, + priority, schedulingPolicy, *this, std::forward<Function>(function), std::forward<Args>(args)...}} +{ + +} + +#endif // def CONFIG_THREAD_DETACH_ENABLE + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_DYNAMICTHREAD_HPP_ diff --git a/include/distortos/DynamicThreadParameters.hpp b/include/distortos/DynamicThreadParameters.hpp new file mode 100644 index 0000000..b27056c --- /dev/null +++ b/include/distortos/DynamicThreadParameters.hpp @@ -0,0 +1,99 @@ +/** + * \file + * \brief DynamicThreadParameters class header + * + * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DYNAMICTHREADPARAMETERS_HPP_ +#define INCLUDE_DISTORTOS_DYNAMICTHREADPARAMETERS_HPP_ + +#include "distortos/SchedulingPolicy.hpp" + +#include <cstddef> + +namespace distortos +{ + +/** + * \brief DynamicThreadParameters struct is a helper with parameters for DynamicThread's constructor + * + * This struct is a replacement for overloads of DynamicThread's constructor, makeDynamicThread() and + * makeAndStartDynamicThread() which - unfortunately - cannot be used, as they would lead to compilation errors due to + * ambiguity. + * + * \ingroup threads + */ + +struct DynamicThreadParameters +{ + /** + * \brief DynamicThreadParameters's constructor + * + * \param [in] stackSizee is the size of stack, bytes + * \param [in] canReceiveSignalss selects whether reception of signals is enabled (true) or disabled (false) for + * this thread + * \param [in] queuedSignalss is the max number of queued signals for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread + * \param [in] signalActionss is the max number of different SignalAction objects for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable catching of signals for this thread + * \param [in] priorityy is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicyy is the scheduling policy of the thread, default - SchedulingPolicy::roundRobin + */ + + constexpr DynamicThreadParameters(const size_t stackSizee, const bool canReceiveSignalss, + const size_t queuedSignalss, const size_t signalActionss, const uint8_t priorityy, + const SchedulingPolicy schedulingPolicyy = SchedulingPolicy::roundRobin) : + queuedSignals{queuedSignalss}, + signalActions{signalActionss}, + stackSize{stackSizee}, + canReceiveSignals{canReceiveSignalss}, + priority{priorityy}, + schedulingPolicy{schedulingPolicyy} + { + + } + + /** + * \brief DynamicThreadParameters's constructor + * + * \param [in] stackSizee is the size of stack, bytes + * \param [in] priorityy is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicyy is the scheduling policy of the thread, default - SchedulingPolicy::roundRobin + */ + + constexpr DynamicThreadParameters(const size_t stackSizee, const uint8_t priorityy, + const SchedulingPolicy schedulingPolicyy = SchedulingPolicy::roundRobin) : + DynamicThreadParameters{stackSizee, false, 0, 0, priorityy, schedulingPolicyy} + { + + } + + /// max number of queued signals for this thread, relevant only if \a canReceiveSignals == true, 0 to disable + /// queuing of signals for this thread + size_t queuedSignals; + + /// max number of different SignalAction objects for this thread, relevant only if \a canReceiveSignals == true, 0 + /// to disable catching of signals for this thread + size_t signalActions; + + /// size of stack, bytes + size_t stackSize; + + /// selects whether reception of signals is enabled (true) or disabled (false) for this thread + bool canReceiveSignals; + + /// thread's priority, 0 - lowest, UINT8_MAX - highest + uint8_t priority; + + /// scheduling policy of the thread + SchedulingPolicy schedulingPolicy; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_DYNAMICTHREADPARAMETERS_HPP_ diff --git a/include/distortos/FifoQueue.hpp b/include/distortos/FifoQueue.hpp new file mode 100644 index 0000000..9f6c952 --- /dev/null +++ b/include/distortos/FifoQueue.hpp @@ -0,0 +1,682 @@ +/** + * \file + * \brief FifoQueue class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_FIFOQUEUE_HPP_ +#define INCLUDE_DISTORTOS_FIFOQUEUE_HPP_ + +#include "distortos/internal/synchronization/FifoQueueBase.hpp" +#include "distortos/internal/synchronization/BoundQueueFunctor.hpp" +#include "distortos/internal/synchronization/CopyConstructQueueFunctor.hpp" +#include "distortos/internal/synchronization/MoveConstructQueueFunctor.hpp" +#include "distortos/internal/synchronization/SwapPopQueueFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreWaitFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreTryWaitFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreTryWaitForFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreTryWaitUntilFunctor.hpp" + +/// GCC 4.9 is needed for all FifoQueue::*emplace*() functions - earlier versions don't support parameter pack expansion +/// in lambdas +#define DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED __GNUC_PREREQ(4, 9) + +namespace distortos +{ + +/** + * \brief FifoQueue class is a simple FIFO queue for thread-thread, thread-interrupt or interrupt-interrupt + * communication. It supports multiple readers and multiple writers. It is implemented as a wrapper for + * internal::FifoQueueBase. + * + * \tparam T is the type of data in queue + * + * \ingroup queues + */ + +template<typename T> +class FifoQueue +{ +public: + + /// type of uninitialized storage for data + using Storage = typename std::aligned_storage<sizeof(T), alignof(T)>::type; + + /// unique_ptr (with deleter) to Storage[] + using StorageUniquePointer = + std::unique_ptr<Storage[], internal::FifoQueueBase::StorageUniquePointer::deleter_type>; + + /** + * \brief FifoQueue's constructor + * + * \param [in] storageUniquePointer is a rvalue reference to StorageUniquePointer with storage for queue elements + * (sufficiently large for \a maxElements, each sizeof(T) bytes long) and appropriate deleter + * \param [in] maxElements is the number of elements in storage array + */ + + FifoQueue(StorageUniquePointer&& storageUniquePointer, const size_t maxElements) : + fifoQueueBase_{{storageUniquePointer.release(), storageUniquePointer.get_deleter()}, sizeof(T), maxElements} + { + + } + + /** + * \brief FifoQueue's destructor + * + * Pops all remaining elements from the queue. + */ + + ~FifoQueue(); + +#if DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Emplaces the element in the queue. + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int emplace(Args&&... args) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return emplaceInternal(semaphoreWaitFunctor, std::forward<Args>(args)...); + } + +#endif // DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Pops the oldest (first) element from the queue. + * + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int pop(T& value) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return popInternal(semaphoreWaitFunctor, value); + } + + /** + * \brief Pushes the element to the queue. + * + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int push(const T& value) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return pushInternal(semaphoreWaitFunctor, value); + } + + /** + * \brief Pushes the element to the queue. + * + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int push(T&& value) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return pushInternal(semaphoreWaitFunctor, std::move(value)); + } + +#if DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Tries to emplace the element in the queue. + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int tryEmplace(Args&&... args) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return emplaceInternal(semaphoreTryWaitFunctor, std::forward<Args>(args)...); + } + + /** + * \brief Tries to emplace the element in the queue for a given duration of time. + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] duration is the duration after which the wait will be terminated without emplacing the element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int tryEmplaceFor(const TickClock::duration duration, Args&&... args) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return emplaceInternal(semaphoreTryWaitForFunctor, std::forward<Args>(args)...); + } + + /** + * \brief Tries to emplace the element in the queue for a given duration of time. + * + * Template variant of FifoQueue::tryEmplaceFor(TickClock::duration, Args&&...). + * + * \note This function requires GCC 4.9. + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * \tparam Args are types of arguments for constructor of T + * + * \param [in] duration is the duration after which the wait will be terminated without emplacing the element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period, typename... Args> + int tryEmplaceFor(const std::chrono::duration<Rep, Period> duration, Args&&... args) + { + return tryEmplaceFor(std::chrono::duration_cast<TickClock::duration>(duration), std::forward<Args>(args)...); + } + + /** + * \brief Tries to emplace the element in the queue until a given time point. + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] timePoint is the time point at which the call will be terminated without emplacing the element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int tryEmplaceUntil(const TickClock::time_point timePoint, Args&&... args) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return emplaceInternal(semaphoreTryWaitUntilFunctor, std::forward<Args>(args)...); + } + + /** + * \brief Tries to emplace the element in the queue until a given time point. + * + * Template variant of FifoQueue::tryEmplaceUntil(TickClock::time_point, Args&&...). + * + * \note This function requires GCC 4.9. + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * \tparam Args are types of arguments for constructor of T + * + * \param [in] timePoint is the time point at which the call will be terminated without emplacing the element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration, typename... Args> + int tryEmplaceUntil(const std::chrono::time_point<TickClock, Duration> timePoint, Args&&... args) + { + return tryEmplaceUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), + std::forward<Args>(args)...); + } + +#endif // DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Tries to pop the oldest (first) element from the queue. + * + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPop(T& value) + { + internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return popInternal(semaphoreTryWaitFunctor, value); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue for a given duration of time. + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPopFor(const TickClock::duration duration, T& value) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return popInternal(semaphoreTryWaitForFunctor, value); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue for a given duration of time. + * + * Template variant of tryPopFor(TickClock::duration, T&). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPopFor(const std::chrono::duration<Rep, Period> duration, T& value) + { + return tryPopFor(std::chrono::duration_cast<TickClock::duration>(duration), value); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue until a given time point. + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPopUntil(const TickClock::time_point timePoint, T& value) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return popInternal(semaphoreTryWaitUntilFunctor, value); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue until a given time point. + * + * Template variant of tryPopUntil(TickClock::time_point, T&). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPopUntil(const std::chrono::time_point<TickClock, Duration> timePoint, T& value) + { + return tryPopUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), value); + } + + /** + * \brief Tries to push the element to the queue. + * + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPush(const T& value) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return pushInternal(semaphoreTryWaitFunctor, value); + } + + /** + * \brief Tries to push the element to the queue. + * + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPush(T&& value) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return pushInternal(semaphoreTryWaitFunctor, std::move(value)); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushFor(const TickClock::duration duration, const T& value) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return pushInternal(semaphoreTryWaitForFunctor, value); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Template variant of tryPushFor(TickClock::duration, const T&). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPushFor(const std::chrono::duration<Rep, Period> duration, const T& value) + { + return tryPushFor(std::chrono::duration_cast<TickClock::duration>(duration), value); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * \param [in] duration is the duration after which the call will be terminated without pushing the element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushFor(const TickClock::duration duration, T&& value) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return pushInternal(semaphoreTryWaitForFunctor, std::move(value)); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Template variant of tryPushFor(TickClock::duration, T&&). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the call will be terminated without pushing the element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPushFor(const std::chrono::duration<Rep, Period> duration, T&& value) + { + return tryPushFor(std::chrono::duration_cast<TickClock::duration>(duration), std::move(value)); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushUntil(const TickClock::time_point timePoint, const T& value) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return pushInternal(semaphoreTryWaitUntilFunctor, value); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Template variant of tryPushUntil(TickClock::time_point, const T&). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPushUntil(const std::chrono::time_point<TickClock, Duration> timePoint, const T& value) + { + return tryPushUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), value); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushUntil(const TickClock::time_point timePoint, T&& value) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return pushInternal(semaphoreTryWaitUntilFunctor, std::move(value)); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Template variant of tryPushUntil(TickClock::time_point, T&&). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPushUntil(const std::chrono::time_point<TickClock, Duration> timePoint, T&& value) + { + return tryPushUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), std::move(value)); + } + +private: + +#if DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Emplaces the element in the queue. + * + * Internal version - builds the Functor object. + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int emplaceInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, Args&&... args); + +#endif // DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Pops the oldest (first) element from the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a popSemaphore_ + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int popInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, T& value); + + /** + * \brief Pushes the element to the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const T& value); + + /** + * \brief Pushes the element to the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, T&& value); + + /// contained internal::FifoQueueBase object which implements whole functionality + internal::FifoQueueBase fifoQueueBase_; +}; + +template<typename T> +FifoQueue<T>::~FifoQueue() +{ + T value; + while (tryPop(value) == 0); +} + +#if DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + +template<typename T> +template<typename... Args> +int FifoQueue<T>::emplaceInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, Args&&... args) +{ + const auto emplaceFunctor = internal::makeBoundQueueFunctor( + [&args...](void* const storage) + { + new (storage) T{std::forward<Args>(args)...}; + }); + return fifoQueueBase_.push(waitSemaphoreFunctor, emplaceFunctor); +} + +#endif // DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + +template<typename T> +int FifoQueue<T>::popInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, T& value) +{ + const internal::SwapPopQueueFunctor<T> swapPopQueueFunctor {value}; + return fifoQueueBase_.pop(waitSemaphoreFunctor, swapPopQueueFunctor); +} + +template<typename T> +int FifoQueue<T>::pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const T& value) +{ + const internal::CopyConstructQueueFunctor<T> copyConstructQueueFunctor {value}; + return fifoQueueBase_.push(waitSemaphoreFunctor, copyConstructQueueFunctor); +} + +template<typename T> +int FifoQueue<T>::pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, T&& value) +{ + const internal::MoveConstructQueueFunctor<T> moveConstructQueueFunctor {std::move(value)}; + return fifoQueueBase_.push(waitSemaphoreFunctor, moveConstructQueueFunctor); +} + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_FIFOQUEUE_HPP_ diff --git a/include/distortos/MessageQueue.hpp b/include/distortos/MessageQueue.hpp new file mode 100644 index 0000000..0f44d8f --- /dev/null +++ b/include/distortos/MessageQueue.hpp @@ -0,0 +1,762 @@ +/** + * \file + * \brief MessageQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_MESSAGEQUEUE_HPP_ +#define INCLUDE_DISTORTOS_MESSAGEQUEUE_HPP_ + +#include "distortos/internal/synchronization/MessageQueueBase.hpp" +#include "distortos/internal/synchronization/BoundQueueFunctor.hpp" +#include "distortos/internal/synchronization/CopyConstructQueueFunctor.hpp" +#include "distortos/internal/synchronization/MoveConstructQueueFunctor.hpp" +#include "distortos/internal/synchronization/SwapPopQueueFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreWaitFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreTryWaitFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreTryWaitForFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreTryWaitUntilFunctor.hpp" + +namespace distortos +{ + +/// GCC 4.9 is needed for all MessageQueue::*emplace*() functions - earlier versions don't support parameter pack +/// expansion in lambdas +#define DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED __GNUC_PREREQ(4, 9) + +/** + * \brief MessageQueue class is a message queue for thread-thread, thread-interrupt or interrupt-interrupt + * communication. It supports multiple readers and multiple writers. It is implemented as a wrapper for + * internal::MessageQueueBase. + * + * Similar to POSIX mqd_t - http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/mqueue.h.html + * + * \tparam T is the type of data in queue + * + * \ingroup queues + */ + +template<typename T> +class MessageQueue +{ +public: + + /// type of uninitialized storage for Entry with link + using EntryStorage = internal::MessageQueueBase::EntryStorage; + + /// type of uninitialized storage for value + using ValueStorage = internal::MessageQueueBase::ValueStorage<T>; + + /// import EntryStorageUniquePointer type from internal::MessageQueueBase class + using EntryStorageUniquePointer = internal::MessageQueueBase::EntryStorageUniquePointer; + + /// unique_ptr (with deleter) to ValueStorage[] + using ValueStorageUniquePointer = + std::unique_ptr<ValueStorage[], internal::MessageQueueBase::ValueStorageUniquePointer::deleter_type>; + + /** + * \brief MessageQueue's constructor + * + * \param [in] entryStorageUniquePointer is a rvalue reference to EntryStorageUniquePointer with storage for queue + * entries (sufficiently large for \a maxElements EntryStorage objects) and appropriate deleter + * \param [in] valueStorageUniquePointer is a rvalue reference to ValueStorageUniquePointer with storage for queue + * elements (sufficiently large for \a maxElements, each sizeof(T) bytes long) and appropriate deleter + * \param [in] maxElements is the number of elements in \a entryStorage and \a valueStorage arrays + */ + + MessageQueue(EntryStorageUniquePointer&& entryStorageUniquePointer, + ValueStorageUniquePointer&& valueStorageUniquePointer, const size_t maxElements) : + messageQueueBase_{std::move(entryStorageUniquePointer), + {valueStorageUniquePointer.release(), valueStorageUniquePointer.get_deleter()}, + sizeof(*valueStorageUniquePointer.get()), maxElements} + { + + } + + /** + * \brief MessageQueue's destructor + * + * Pops all remaining elements from the queue. + */ + + ~MessageQueue(); + +#if DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Emplaces the element in the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] priority is the priority of new element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int emplace(const uint8_t priority, Args&&... args) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return emplaceInternal(semaphoreWaitFunctor, priority, std::forward<Args>(args)...); + } + +#endif // DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Pops oldest element with highest priority from the queue. + * + * Similar to mq_receive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int pop(uint8_t& priority, T& value) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return popInternal(semaphoreWaitFunctor, priority, value); + } + + /** + * \brief Pushes the element to the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] priority is the priority of new element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int push(const uint8_t priority, const T& value) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return pushInternal(semaphoreWaitFunctor, priority, value); + } + + /** + * \brief Pushes the element to the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] priority is the priority of new element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int push(const uint8_t priority, T&& value) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return pushInternal(semaphoreWaitFunctor, priority, std::move(value)); + } + +#if DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Tries to emplace the element in the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] priority is the priority of new element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int tryEmplace(const uint8_t priority, Args&&... args) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return emplaceInternal(semaphoreTryWaitFunctor, priority, std::forward<Args>(args)...); + } + + /** + * \brief Tries to emplace the element in the queue for a given duration of time. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] duration is the duration after which the wait will be terminated without emplacing the element + * \param [in] priority is the priority of new element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int tryEmplaceFor(const TickClock::duration duration, const uint8_t priority, Args&&... args) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return emplaceInternal(semaphoreTryWaitForFunctor, priority, std::forward<Args>(args)...); + } + + /** + * \brief Tries to emplace the element in the queue for a given duration of time. + * + * Template variant of MessageQueue::tryEmplaceFor(TickClock::duration, uint8_t, Args&&...). + * + * \note This function requires GCC 4.9. + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * \tparam Args are types of arguments for constructor of T + * + * \param [in] duration is the duration after which the wait will be terminated without emplacing the element + * \param [in] priority is the priority of new element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period, typename... Args> + int tryEmplaceFor(const std::chrono::duration<Rep, Period> duration, const uint8_t priority, Args&&... args) + { + return tryEmplaceFor(std::chrono::duration_cast<TickClock::duration>(duration), priority, + std::forward<Args>(args)...); + } + + /** + * \brief Tries to emplace the element in the queue until a given time point. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] timePoint is the time point at which the call will be terminated without emplacing the element + * \param [in] priority is the priority of new element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int tryEmplaceUntil(const TickClock::time_point timePoint, const uint8_t priority, Args&&... args) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return emplaceInternal(semaphoreTryWaitUntilFunctor, priority, std::forward<Args>(args)...); + } + + /** + * \brief Tries to emplace the element in the queue until a given time point. + * + * Template variant of FifoQueue::tryEmplaceUntil(TickClock::time_point, uint8_t, Args&&...). + * + * \note This function requires GCC 4.9. + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * \tparam Args are types of arguments for constructor of T + * + * \param [in] timePoint is the time point at which the call will be terminated without emplacing the element + * \param [in] priority is the priority of new element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration, typename... Args> + int tryEmplaceUntil(const std::chrono::time_point<TickClock, Duration> timePoint, const uint8_t priority, + Args&&... args) + { + return tryEmplaceUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), priority, + std::forward<Args>(args)...); + } + +#endif // DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Tries to pop oldest element with highest priority from the queue. + * + * Similar to mq_receive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPop(uint8_t& priority, T& value) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return popInternal(semaphoreTryWaitFunctor, priority, value); + } + + /** + * \brief Tries to pop oldest element with highest priority from the queue for a given duration of time. + * + * Similar to mq_timedreceive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPopFor(const TickClock::duration duration, uint8_t& priority, T& value) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return popInternal(semaphoreTryWaitForFunctor, priority, value); + } + + /** + * \brief Tries to pop oldest element with highest priority from the queue for a given duration of time. + * + * Template variant of tryPopFor(TickClock::duration, uint8_t&, T&). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPopFor(const std::chrono::duration<Rep, Period> duration, uint8_t& priority, T& value) + { + return tryPopFor(std::chrono::duration_cast<TickClock::duration>(duration), priority, value); + } + + /** + * \brief Tries to pop oldest element with highest priority from the queue until a given time point. + * + * Similar to mq_timedreceive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPopUntil(const TickClock::time_point timePoint, uint8_t& priority, T& value) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return popInternal(semaphoreTryWaitUntilFunctor, priority, value); + } + + /** + * \brief Tries to pop oldest element with highest priority from the queue until a given time point. + * + * Template variant of tryPopUntil(TickClock::time_point, uint8_t&, T&). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPopUntil(const std::chrono::time_point<TickClock, Duration> timePoint, uint8_t& priority, T& value) + { + return tryPopUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), priority, value); + } + + /** + * \brief Tries to push the element to the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] priority is the priority of new element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPush(const uint8_t priority, const T& value) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return pushInternal(semaphoreTryWaitFunctor, priority, value); + } + + /** + * \brief Tries to push the element to the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] priority is the priority of new element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPush(const uint8_t priority, T&& value) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return pushInternal(semaphoreTryWaitFunctor, priority, std::move(value)); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushFor(const TickClock::duration duration, const uint8_t priority, const T& value) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return pushInternal(semaphoreTryWaitForFunctor, priority, value); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Template variant of tryPushFor(TickClock::duration, uint8_t, const T&). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPushFor(const std::chrono::duration<Rep, Period> duration, const uint8_t priority, const T& value) + { + return tryPushFor(std::chrono::duration_cast<TickClock::duration>(duration), priority, value); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushFor(const TickClock::duration duration, const uint8_t priority, T&& value) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return pushInternal(semaphoreTryWaitForFunctor, priority, std::move(value)); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Template variant of tryPushFor(TickClock::duration, uint8_t, T&&). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the call will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPushFor(const std::chrono::duration<Rep, Period> duration, const uint8_t priority, T&& value) + { + return tryPushFor(std::chrono::duration_cast<TickClock::duration>(duration), priority, std::move(value)); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushUntil(const TickClock::time_point timePoint, const uint8_t priority, const T& value) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return pushInternal(semaphoreTryWaitUntilFunctor, priority, value); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Template variant of tryPushUntil(TickClock::time_point, uint8_t, const T&). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPushUntil(const std::chrono::time_point<TickClock, Duration> timePoint, const uint8_t priority, + const T& value) + { + return tryPushUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), priority, value); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushUntil(const TickClock::time_point timePoint, const uint8_t priority, T&& value) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return pushInternal(semaphoreTryWaitUntilFunctor, priority, std::move(value)); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Template variant of tryPushUntil(TickClock::time_point, uint8_t, T&&). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPushUntil(const std::chrono::time_point<TickClock, Duration> timePoint, const uint8_t priority, T&& value) + { + return tryPushUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), priority, std::move(value)); + } + +private: + +#if DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Emplaces the element in the queue. + * + * Internal version - builds the Functor object. + * + * \note This function requires GCC 4.9. + * + * \tparam Args are types of arguments for constructor of T + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] priority is the priority of new element + * \param [in] args are arguments for constructor of T + * + * \return zero if element was emplaced successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + template<typename... Args> + int emplaceInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, uint8_t priority, Args&&... args); + +#endif // DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + + /** + * \brief Pops oldest element with highest priority from the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a popSemaphore_ + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int popInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, uint8_t& priority, T& value); + + /** + * \brief Pushes the element to the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] priority is the priority of new element + * \param [in] value is a reference to object that will be pushed, value in queue's storage is copy-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, uint8_t priority, const T& value); + + /** + * \brief Pushes the element to the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] priority is the priority of new element + * \param [in] value is a rvalue reference to object that will be pushed, value in queue's storage is + * move-constructed + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, uint8_t priority, T&& value); + + /// contained internal::MessageQueueBase object which implements whole functionality + internal::MessageQueueBase messageQueueBase_; +}; + +template<typename T> +MessageQueue<T>::~MessageQueue() +{ + uint8_t priority; + T value; + while (tryPop(priority, value) == 0); +} + +#if DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + +template<typename T> +template<typename... Args> +int MessageQueue<T>::emplaceInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const uint8_t priority, + Args&&... args) +{ + const auto emplaceFunctor = internal::makeBoundQueueFunctor( + [&args...](void* const storage) + { + new (storage) T{std::forward<Args>(args)...}; + }); + return messageQueueBase_.push(waitSemaphoreFunctor, priority, emplaceFunctor); +} + +#endif // DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + +template<typename T> +int MessageQueue<T>::popInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, uint8_t& priority, T& value) +{ + const internal::SwapPopQueueFunctor<T> swapPopQueueFunctor {value}; + return messageQueueBase_.pop(waitSemaphoreFunctor, priority, swapPopQueueFunctor); +} + +template<typename T> +int MessageQueue<T>::pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const uint8_t priority, + const T& value) +{ + const internal::CopyConstructQueueFunctor<T> copyConstructQueueFunctor {value}; + return messageQueueBase_.push(waitSemaphoreFunctor, priority, copyConstructQueueFunctor); +} + +template<typename T> +int MessageQueue<T>::pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const uint8_t priority, + T&& value) +{ + const internal::MoveConstructQueueFunctor<T> moveConstructQueueFunctor {std::move(value)}; + return messageQueueBase_.push(waitSemaphoreFunctor, priority, moveConstructQueueFunctor); +} + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_MESSAGEQUEUE_HPP_ diff --git a/include/distortos/Mutex.hpp b/include/distortos/Mutex.hpp new file mode 100644 index 0000000..481700d --- /dev/null +++ b/include/distortos/Mutex.hpp @@ -0,0 +1,280 @@ +/** + * \file + * \brief Mutex class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_MUTEX_HPP_ +#define INCLUDE_DISTORTOS_MUTEX_HPP_ + +#include "distortos/internal/synchronization/MutexControlBlock.hpp" + +namespace distortos +{ + +/** + * \brief Mutex is the basic synchronization primitive + * + * Similar to std::mutex - http://en.cppreference.com/w/cpp/thread/mutex + * Similar to POSIX pthread_mutex_t - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09 -> 2.9.3 Thread Mutexes + * + * \ingroup synchronization + */ + +class Mutex +{ +public: + + /// mutex protocols + using Protocol = internal::MutexControlBlock::Protocol; + + /// type used for counting recursive locks + using RecursiveLocksCount = uint16_t; + + /// type of mutex + enum class Type : uint8_t + { + /// normal mutex, similar to PTHREAD_MUTEX_NORMAL + normal, + /// mutex with additional error checking, similar to PTHREAD_MUTEX_ERRORCHECK + errorChecking, + /// recursive mutex, similar to PTHREAD_MUTEX_RECURSIVE + recursive + }; + + /** + * \brief Gets the maximum number of recursive locks possible before returning EAGAIN + * + * \note Actual number of lock() operations possible is getMaxRecursiveLocks() + 1. + * + * \return maximum number of recursive locks possible before returning EAGAIN + */ + + constexpr static RecursiveLocksCount getMaxRecursiveLocks() + { + return std::numeric_limits<RecursiveLocksCount>::max(); + } + + /** + * \brief Mutex constructor + * + * Similar to std::mutex::mutex() - http://en.cppreference.com/w/cpp/thread/mutex/mutex + * Similar to pthread_mutex_init() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html + * + * \param [in] type is the type of mutex, default - Type::normal + * \param [in] protocol is the mutex protocol, default - Protocol::none + * \param [in] priorityCeiling is the priority ceiling of mutex, ignored when protocol != Protocol::priorityProtect, + * default - 0 + */ + + constexpr explicit Mutex(const Type type = Type::normal, const Protocol protocol = Protocol::none, + const uint8_t priorityCeiling = {}) : + controlBlock_{protocol, priorityCeiling}, + recursiveLocksCount_{}, + type_{type} + { + + } + + /** + * \brief Locks the mutex. + * + * Similar to std::mutex::lock() - http://en.cppreference.com/w/cpp/thread/mutex/lock + * Similar to pthread_mutex_lock() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html# + * + * If the mutex is already locked by another thread, the calling thread shall block until the mutex becomes + * available. This function shall return with the mutex in the locked state with the calling thread as its owner. If + * a thread attempts to relock a mutex that it has already locked, deadlock occurs. + * + * \return zero if the caller successfully locked the mutex, error code otherwise: + * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been + * exceeded; + * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; + * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling + * thread's priority is higher than the mutex's current priority ceiling; + */ + + int lock(); + + /** + * \brief Tries to lock the mutex. + * + * Similar to std::mutex::try_lock() - http://en.cppreference.com/w/cpp/thread/mutex/try_lock + * Similar to pthread_mutex_trylock() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html# + * + * This function shall be equivalent to lock(), except that if the mutex is currently locked (by any thread, + * including the current thread), the call shall return immediately. + * + * \return zero if the caller successfully locked the mutex, error code otherwise: + * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been + * exceeded; + * - EBUSY - the mutex could not be acquired because it was already locked; + * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling + * thread's priority is higher than the mutex's current priority ceiling; + */ + + int tryLock(); + + /** + * \brief Tries to lock the mutex for given duration of time. + * + * Similar to std::timed_mutex::try_lock_for() - http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_for + * Similar to pthread_mutex_timedlock() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html# + * + * If the mutex is already locked, the calling thread shall block until the mutex becomes available as in lock() + * function. If the mutex cannot be locked without waiting for another thread to unlock the mutex, this wait shall + * be terminated when the specified timeout expires. + * + * Under no circumstance shall the function fail with a timeout if the mutex can be locked immediately. The validity + * of the duration parameter need not be checked if the mutex can be locked immediately. + * + * \param [in] duration is the duration after which the wait will be terminated without locking the mutex + * + * \return zero if the caller successfully locked the mutex, error code otherwise: + * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been + * exceeded; + * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; + * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling + * thread's priority is higher than the mutex's current priority ceiling; + * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired; + */ + + int tryLockFor(TickClock::duration duration); + + /** + * Tries to lock the mutex for given duration of time. + * + * Template variant of tryLockFor(TickClock::duration duration). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the wait will be terminated without locking the mutex + * + * \return zero if the caller successfully locked the mutex, error code otherwise: + * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been + * exceeded; + * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; + * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling + * thread's priority is higher than the mutex's current priority ceiling; + * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired; + */ + + template<typename Rep, typename Period> + int tryLockFor(const std::chrono::duration<Rep, Period> duration) + { + return tryLockFor(std::chrono::duration_cast<TickClock::duration>(duration)); + } + + /** + * \brief Tries to lock the mutex until given time point. + * + * Similar to std::timed_mutex::try_lock_until() - + * http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_until + * Similar to pthread_mutex_timedlock() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html# + * + * If the mutex is already locked, the calling thread shall block until the mutex becomes available as in lock() + * function. If the mutex cannot be locked without waiting for another thread to unlock the mutex, this wait shall + * be terminated when the specified timeout expires. + * + * Under no circumstance shall the function fail with a timeout if the mutex can be locked immediately. The validity + * of the timePoint parameter need not be checked if the mutex can be locked immediately. + * + * \param [in] timePoint is the time point at which the wait will be terminated without locking the mutex + * + * \return zero if the caller successfully locked the mutex, error code otherwise: + * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been + * exceeded; + * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; + * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling + * thread's priority is higher than the mutex's current priority ceiling; + * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired; + */ + + int tryLockUntil(TickClock::time_point timePoint); + + /** + * \brief Tries to lock the mutex until given time point. + * + * Template variant of tryLockUntil(TickClock::time_point timePoint). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the wait will be terminated without locking the mutex + * + * \return zero if the caller successfully locked the mutex, error code otherwise: + * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been + * exceeded; + * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; + * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling + * thread's priority is higher than the mutex's current priority ceiling; + * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired; + */ + + template<typename Duration> + int tryLockUntil(const std::chrono::time_point<TickClock, Duration> timePoint) + { + return tryLockUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint)); + } + + /** + * \brief Unlocks the mutex. + * + * Similar to std::mutex::unlock() - http://en.cppreference.com/w/cpp/thread/mutex/unlock + * Similar to pthread_mutex_unlock() - + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html# + * + * The mutex must be locked by the current thread, otherwise, the behavior is undefined. If there are threads + * blocked on this mutex, the highest priority waiting thread shall be unblocked, and if there is more than one + * highest priority thread blocked waiting, then the highest priority thread that has been waiting the longest shall + * be unblocked. + * + * \return zero if the caller successfully unlocked the mutex, error code otherwise: + * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; + */ + + int unlock(); + +private: + + /** + * \brief Internal version of tryLock(). + * + * Internal version with no interrupt masking and additional code for ErrorChecking type (which is not required for + * tryLock()). + * + * \return zero if the caller successfully locked the mutex, error code otherwise: + * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been + * exceeded; + * - EBUSY - the mutex could not be acquired because it was already locked; + * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; + * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling + * thread's priority is higher than the mutex's current priority ceiling; + */ + + int tryLockInternal(); + + /// instance of control block + internal::MutexControlBlock controlBlock_; + + /// number of recursive locks, used when mutex type is Recursive + RecursiveLocksCount recursiveLocksCount_; + + /// type of mutex + Type type_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_MUTEX_HPP_ diff --git a/include/distortos/OnceFlag.hpp b/include/distortos/OnceFlag.hpp new file mode 100644 index 0000000..0ac6185 --- /dev/null +++ b/include/distortos/OnceFlag.hpp @@ -0,0 +1,60 @@ +/** + * \file + * \brief OnceFlag class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ONCEFLAG_HPP_ +#define INCLUDE_DISTORTOS_ONCEFLAG_HPP_ + +#include "distortos/internal/synchronization/CallOnceControlBlock.hpp" + +#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1 + +namespace distortos +{ + +/** + * \brief OnceFlag is a helper class for callOnce(). + * + * Similar to std::once_flag - http://en.cppreference.com/w/cpp/thread/once_flag + * Similar to POSIX pthread_once_t - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_once.html# + * + * \note This class requires GCC 4.9. + * + * \ingroup synchronization + */ + +class OnceFlag +{ + template<typename Function, typename... Args> + friend void callOnce(OnceFlag& onceFlag, Function&& function, Args&&... args); + +public: + + /** + * \brief OnceFlag's constructor + */ + + constexpr OnceFlag() : + callOnceControlBlock_{} + { + + } + +private: + + /// internal internal::CallOnceControlBlock object used by callOnce() + internal::CallOnceControlBlock callOnceControlBlock_; +}; + +} // namespace distortos + +#endif // DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1 + +#endif // INCLUDE_DISTORTOS_ONCEFLAG_HPP_ diff --git a/include/distortos/RawFifoQueue.hpp b/include/distortos/RawFifoQueue.hpp new file mode 100644 index 0000000..94456a7 --- /dev/null +++ b/include/distortos/RawFifoQueue.hpp @@ -0,0 +1,463 @@ +/** + * \file + * \brief RawFifoQueue class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_RAWFIFOQUEUE_HPP_ +#define INCLUDE_DISTORTOS_RAWFIFOQUEUE_HPP_ + +#include "distortos/internal/synchronization/FifoQueueBase.hpp" + +namespace distortos +{ + +/** + * \brief RawFifoQueue class is very similar to FifoQueue, but optimized for binary serializable types (like POD types). + * + * Type \a T can be used with both RawFifoQueue and FifoQueue only when + * <em>std::is_trivially_copyable<T>::value == true</em>, otherwise only FifoQueue use is safe, while using RawFifoQueue + * results in undefined behavior. + * + * \ingroup queues + */ + +class RawFifoQueue +{ +public: + + /// unique_ptr (with deleter) to storage + using StorageUniquePointer = internal::FifoQueueBase::StorageUniquePointer; + + /** + * \brief RawFifoQueue's constructor + * + * \param [in] storageUniquePointer is a rvalue reference to StorageUniquePointer with storage for queue elements + * (sufficiently large for \a maxElements, each \a elementSize bytes long) and appropriate deleter + * \param [in] elementSize is the size of single queue element, bytes + * \param [in] maxElements is the number of elements in storage memory block + */ + + RawFifoQueue(StorageUniquePointer&& storageUniquePointer, size_t elementSize, size_t maxElements); + + /** + * \brief Pops the oldest (first) element from the queue. + * + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int pop(void* buffer, size_t size); + + /** + * \brief Pops the oldest (first) element from the queue. + * + * \tparam T is the type of data popped from the queue + * + * \param [out] buffer is a reference to object that will be used to return popped value + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename T> + int pop(T& buffer) + { + return pop(&buffer, sizeof(buffer)); + } + + /** + * \brief Pushes the element to the queue. + * + * \param [in] data is a pointer to data that will be pushed to RawFifoQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int push(const void* data, size_t size); + + /** + * \brief Pushes the element to the queue. + * + * \tparam T is the type of data pushed to the queue + * + * \param [in] data is a reference to data that will be pushed to RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename T> + int push(const T& data) + { + return push(&data, sizeof(data)); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue. + * + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPop(void* buffer, size_t size); + + /** + * \brief Tries to pop the oldest (first) element from the queue. + * + * \tparam T is the type of data popped from the queue + * + * \param [out] buffer is a reference to object that will be used to return popped value + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename T> + int tryPop(T& buffer) + { + return tryPop(&buffer, sizeof(buffer)); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue for a given duration of time. + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPopFor(TickClock::duration duration, void* buffer, size_t size); + + /** + * \brief Tries to pop the oldest (first) element from the queue for a given duration of time. + * + * Template variant of tryPopFor(TickClock::duration, void*, size_t). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPopFor(const std::chrono::duration<Rep, Period> duration, void* const buffer, const size_t size) + { + return tryPopFor(std::chrono::duration_cast<TickClock::duration>(duration), buffer, size); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue for a given duration of time. + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * \tparam T is the type of data popped from the queue + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] buffer is a reference to object that will be used to return popped value + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period, typename T> + int tryPopFor(const std::chrono::duration<Rep, Period> duration, T& buffer) + { + return tryPopFor(std::chrono::duration_cast<TickClock::duration>(duration), &buffer, sizeof(buffer)); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue until a given time point. + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPopUntil(TickClock::time_point timePoint, void* buffer, size_t size); + + /** + * \brief Tries to pop the oldest (first) element from the queue until a given time point. + * + * Template variant of tryPopUntil(TickClock::time_point, void*, size_t). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPopUntil(const std::chrono::time_point<TickClock, Duration> timePoint, void* const buffer, const size_t size) + { + return tryPopUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), buffer, size); + } + + /** + * \brief Tries to pop the oldest (first) element from the queue until a given time point. + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * \tparam T is the type of data popped from the queue + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] buffer is a reference to object that will be used to return popped value + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration, typename T> + int tryPopUntil(const std::chrono::time_point<TickClock, Duration> timePoint, T& buffer) + { + return tryPopUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), &buffer, sizeof(buffer)); + } + + /** + * \brief Tries to push the element to the queue. + * + * \param [in] data is a pointer to data that will be pushed to RawFifoQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPush(const void* data, size_t size); + + /** + * \brief Tries to push the element to the queue. + * + * \tparam T is the type of data pushed to the queue + * + * \param [in] data is a reference to data that will be pushed to RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename T> + int tryPush(const T& data) + { + return tryPush(&data, sizeof(data)); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] data is a pointer to data that will be pushed to RawFifoQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushFor(TickClock::duration duration, const void* data, size_t size); + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Template variant of tryPushFor(TickClock::duration, const void*, size_t). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] data is a pointer to data that will be pushed to RawFifoQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPushFor(const std::chrono::duration<Rep, Period> duration, const void* const data, const size_t size) + { + return tryPushFor(std::chrono::duration_cast<TickClock::duration>(duration), data, size); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * \tparam T is the type of data pushed to the queue + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] data is a reference to data that will be pushed to RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period, typename T> + int tryPushFor(const std::chrono::duration<Rep, Period> duration, const T& data) + { + return tryPushFor(std::chrono::duration_cast<TickClock::duration>(duration), &data, sizeof(data)); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] data is a pointer to data that will be pushed to RawFifoQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushUntil(TickClock::time_point timePoint, const void* data, size_t size); + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Template variant of tryPushUntil(TickClock::time_point, const void*, size_t). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] data is a pointer to data that will be pushed to RawFifoQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPushUntil(const std::chrono::time_point<TickClock, Duration> timePoint, const void* const data, + const size_t size) + { + return tryPushUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), data, size); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * \tparam T is the type of data pushed to the queue + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] data is a reference to data that will be pushed to RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration, typename T> + int tryPushUntil(const std::chrono::time_point<TickClock, Duration> timePoint, const T& data) + { + return tryPushUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), &data, sizeof(data)); + } + +private: + + /** + * \brief Pops the oldest (first) element from the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a popSemaphore_ + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int popInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, void* buffer, size_t size); + + /** + * \brief Pushes the element to the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] data is a pointer to data that will be pushed to RawFifoQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawFifoQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawFifoQueue; + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const void* data, size_t size); + + /// contained internal::FifoQueueBase object which implements base functionality + internal::FifoQueueBase fifoQueueBase_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_RAWFIFOQUEUE_HPP_ diff --git a/include/distortos/RawMessageQueue.hpp b/include/distortos/RawMessageQueue.hpp new file mode 100644 index 0000000..d4b2a54 --- /dev/null +++ b/include/distortos/RawMessageQueue.hpp @@ -0,0 +1,555 @@ +/** + * \file + * \brief RawMessageQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_RAWMESSAGEQUEUE_HPP_ +#define INCLUDE_DISTORTOS_RAWMESSAGEQUEUE_HPP_ + +#include "distortos/internal/synchronization/MessageQueueBase.hpp" + +namespace distortos +{ + +/** + * \brief RawMessageQueue class is very similar to MessageQueue, but optimized for binary serializable types (like POD + * types). + * + * Type \a T can be used with both RawMessageQueue and MessageQueue only when + * <em>std::is_trivially_copyable<T>::value == true</em>, otherwise only MessageQueue use is safe, while using + * RawMessageQueue results in undefined behavior. + * + * Similar to POSIX mqd_t - http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/mqueue.h.html + * + * \ingroup queues + */ + +class RawMessageQueue +{ +public: + + /// type of uninitialized storage for Entry with link + using EntryStorage = internal::MessageQueueBase::EntryStorage; + + /// import EntryStorageUniquePointer type from internal::MessageQueueBase class + using EntryStorageUniquePointer = internal::MessageQueueBase::EntryStorageUniquePointer; + + /** + * type of uninitialized storage for value + * + * \tparam T is the type of data in queue + */ + + template<typename T> + using ValueStorage = internal::MessageQueueBase::ValueStorage<T>; + + using ValueStorageUniquePointer = internal::MessageQueueBase::ValueStorageUniquePointer; + + /** + * \brief RawMessageQueue's constructor + * + * \param [in] entryStorageUniquePointer is a rvalue reference to EntryStorageUniquePointer with storage for queue + * entries (sufficiently large for \a maxElements EntryStorage objects) and appropriate deleter + * \param [in] valueStorageUniquePointer is a rvalue reference to ValueStorageUniquePointer with storage for queue + * elements (sufficiently large for \a maxElements, each \a elementSize bytes long) and appropriate deleter + * \param [in] elementSize is the size of single queue element, bytes + * \param [in] maxElements is the number of elements in \a entryStorage array and \a valueStorage memory block + */ + + RawMessageQueue(EntryStorageUniquePointer&& entryStorageUniquePointer, + ValueStorageUniquePointer&& valueStorageUniquePointer, size_t elementSize, size_t maxElements); + + /** + * \brief Pops oldest element with highest priority from the queue. + * + * Similar to mq_receive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of + * RawMessageQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int pop(uint8_t& priority, void* buffer, size_t size); + + /** + * \brief Pops oldest element with highest priority from the queue. + * + * Similar to mq_receive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \tparam T is the type of data popped from the queue + * + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a reference to object that will be used to return popped value + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename T> + int pop(uint8_t& priority, T& buffer) + { + return pop(priority, &buffer, sizeof(buffer)); + } + + /** + * \brief Pushes the element to the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] priority is the priority of new element + * \param [in] data is a pointer to data that will be pushed to RawMessageQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + int push(uint8_t priority, const void* data, size_t size); + + /** + * \brief Pushes the element to the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \tparam T is the type of data pushed to the queue + * + * \param [in] priority is the priority of new element + * \param [in] data is a reference to data that will be pushed to RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::wait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename T> + int push(const uint8_t priority, const T& data) + { + return push(priority, &data, sizeof(data)); + } + + /** + * \brief Tries to pop the oldest element with highest priority from the queue. + * + * Similar to mq_receive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of + * RawMessageQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPop(uint8_t& priority, void* buffer, size_t size); + + /** + * \brief Tries to pop the oldest element with highest priority from the queue. + * + * Similar to mq_receive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \tparam T is the type of data popped from the queue + * + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a reference to object that will be used to return popped value + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename T> + int tryPop(uint8_t& priority, T& buffer) + { + return tryPop(priority, &buffer, sizeof(buffer)); + } + + /** + * \brief Tries to pop the oldest element with highest priority from the queue for a given duration of time. + * + * Similar to mq_timedreceive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of + * RawMessageQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPopFor(TickClock::duration duration, uint8_t& priority, void* buffer, size_t size); + + /** + * \brief Tries to pop the oldest element with highest priority from the queue for a given duration of time. + * + * Template variant of tryPopFor(TickClock::duration, uint8_t&, void*, size_t). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of + * RawMessageQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPopFor(const std::chrono::duration<Rep, Period> duration, uint8_t& priority, void* const buffer, + const size_t size) + { + return tryPopFor(std::chrono::duration_cast<TickClock::duration>(duration), priority, buffer, size); + } + + /** + * \brief Tries to pop the oldest element with highest priority from the queue for a given duration of time. + * + * Similar to mq_timedreceive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * \tparam T is the type of data popped from the queue + * + * \param [in] duration is the duration after which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a reference to object that will be used to return popped value + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period, typename T> + int tryPopFor(const std::chrono::duration<Rep, Period> duration, uint8_t& priority, T& buffer) + { + return tryPopFor(std::chrono::duration_cast<TickClock::duration>(duration), priority, &buffer, sizeof(buffer)); + } + + /** + * \brief Tries to pop the oldest element with highest priority from the queue until a given time point. + * + * Similar to mq_timedreceive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of + * RawMessageQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPopUntil(TickClock::time_point timePoint, uint8_t& priority, void* buffer, size_t size); + + /** + * \brief Tries to pop the oldest element with highest priority from the queue until a given time point. + * + * Template variant of tryPopUntil(TickClock::time_point, uint8_t&, void*, size_t). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of + * RawMessageQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPopUntil(const std::chrono::time_point<TickClock, Duration> timePoint, uint8_t& priority, void* const buffer, + const size_t size) + { + return tryPopUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), priority, buffer, size); + } + + /** + * \brief Tries to pop the oldest element with highest priority from the queue until a given time point. + * + * Similar to mq_timedreceive() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html# + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * \tparam T is the type of data popped from the queue + * + * \param [in] timePoint is the time point at which the call will be terminated without popping the element + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a reference to object that will be used to return popped value + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration, typename T> + int tryPopUntil(const std::chrono::time_point<TickClock, Duration> timePoint, uint8_t& priority, T& buffer) + { + return tryPopUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), priority, &buffer, + sizeof(buffer)); + } + + /** + * \brief Tries to push the element to the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] priority is the priority of new element + * \param [in] data is a pointer to data that will be pushed to RawMessageQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + int tryPush(uint8_t priority, const void* data, size_t size); + + /** + * \brief Tries to push the element to the queue. + * + * Similar to mq_send() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \tparam T is the type of data pushed to the queue + * + * \param [in] priority is the priority of new element + * \param [in] data is a reference to data that will be pushed to RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWait(); + * - error codes returned by Semaphore::post(); + */ + + template<typename T> + int tryPush(const uint8_t priority, const T& data) + { + return tryPush(priority, &data, sizeof(data)); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] data is a pointer to data that will be pushed to RawMessageQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushFor(TickClock::duration duration, uint8_t priority, const void* data, size_t size); + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Template variant of tryPushFor(TickClock::duration, uint8_t, const void*, size_t). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] data is a pointer to data that will be pushed to RawMessageQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period> + int tryPushFor(const std::chrono::duration<Rep, Period> duration, const uint8_t priority, const void* const data, + const size_t size) + { + return tryPushFor(std::chrono::duration_cast<TickClock::duration>(duration), priority, data, size); + } + + /** + * \brief Tries to push the element to the queue for a given duration of time. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * \tparam T is the type of data pushed to the queue + * + * \param [in] duration is the duration after which the wait will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] data is a reference to data that will be pushed to RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitFor(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Rep, typename Period, typename T> + int tryPushFor(const std::chrono::duration<Rep, Period> duration, const uint8_t priority, const T& data) + { + return tryPushFor(std::chrono::duration_cast<TickClock::duration>(duration), priority, &data, sizeof(data)); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] data is a pointer to data that will be pushed to RawMessageQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + int tryPushUntil(TickClock::time_point timePoint, uint8_t priority, const void* data, size_t size); + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Template variant of tryPushUntil(TickClock::time_point, uint8_t, const void*, size_t). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] data is a pointer to data that will be pushed to RawMessageQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration> + int tryPushUntil(const std::chrono::time_point<TickClock, Duration> timePoint, const uint8_t priority, + const void* const data, const size_t size) + { + return tryPushUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), priority, data, size); + } + + /** + * \brief Tries to push the element to the queue until a given time point. + * + * Similar to mq_timedsend() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html# + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * \tparam T is the type of data pushed to the queue + * + * \param [in] timePoint is the time point at which the call will be terminated without pushing the element + * \param [in] priority is the priority of new element + * \param [in] data is a reference to data that will be pushed to RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - sizeof(T) doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by Semaphore::tryWaitUntil(); + * - error codes returned by Semaphore::post(); + */ + + template<typename Duration, typename T> + int tryPushUntil(const std::chrono::time_point<TickClock, Duration> timePoint, const uint8_t priority, + const T& data) + { + return tryPushUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint), priority, &data, + sizeof(data)); + } + +private: + + /** + * \brief Pops oldest element with highest priority from the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a popSemaphore_ + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes - must be equal to the \a elementSize attribute of + * RawMessageQueue + * + * \return zero if element was popped successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int popInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, uint8_t& priority, void* buffer, + size_t size); + + /** + * \brief Pushes the element to the queue. + * + * Internal version - builds the Functor object. + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] priority is the priority of new element + * \param [in] data is a pointer to data that will be pushed to RawMessageQueue + * \param [in] size is the size of \a data, bytes - must be equal to the \a elementSize attribute of RawMessageQueue + * + * \return zero if element was pushed successfully, error code otherwise: + * - EMSGSIZE - \a size doesn't match the \a elementSize attribute of RawMessageQueue; + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, uint8_t priority, const void* data, + size_t size); + + /// contained internal::MessageQueueBase object which implements base functionality + internal::MessageQueueBase messageQueueBase_; + + /// size of single queue element, bytes + const size_t elementSize_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_RAWMESSAGEQUEUE_HPP_ diff --git a/include/distortos/SchedulingPolicy.hpp b/include/distortos/SchedulingPolicy.hpp new file mode 100644 index 0000000..8793fb4 --- /dev/null +++ b/include/distortos/SchedulingPolicy.hpp @@ -0,0 +1,36 @@ +/** + * \file + * \brief SchedulingPolicy enum class header + * + * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SCHEDULINGPOLICY_HPP_ +#define INCLUDE_DISTORTOS_SCHEDULINGPOLICY_HPP_ + +#include <cstdint> + +namespace distortos +{ + +/** + * \brief scheduling policy of the thread + * + * \ingroup threads + */ + +enum class SchedulingPolicy : uint8_t +{ + /// FIFO scheduling policy + fifo, + /// round-robin scheduling policy + roundRobin, +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SCHEDULINGPOLICY_HPP_ diff --git a/include/distortos/Semaphore.hpp b/include/distortos/Semaphore.hpp new file mode 100644 index 0000000..26ca298 --- /dev/null +++ b/include/distortos/Semaphore.hpp @@ -0,0 +1,245 @@ +/** + * \file + * \brief Semaphore class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SEMAPHORE_HPP_ +#define INCLUDE_DISTORTOS_SEMAPHORE_HPP_ + +#include "distortos/internal/scheduler/ThreadList.hpp" + +#include "distortos/TickClock.hpp" + +namespace distortos +{ + +/** + * \brief Semaphore is the basic synchronization primitive + * + * Similar to POSIX semaphores - http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16 + * + * \ingroup synchronization + */ + +class Semaphore +{ +public: + + /// type used for semaphore's "value" + using Value = unsigned int; + + /** + * \brief Semaphore constructor + * + * Similar to sem_init() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html# + * + * \param [in] value is the initial value of the semaphore, if this value is greater than maxValue, it will be + * truncated + * \param [in] maxValue is the max value of the semaphore before post() returns EOVERFLOW, default - max for Value + * type + */ + + constexpr explicit Semaphore(const Value value, const Value maxValue = std::numeric_limits<Value>::max()) : + blockedList_{}, + value_{value <= maxValue ? value : maxValue}, + maxValue_{maxValue} + { + + } + + /** + * \brief Semaphore destructor + * + * Similar to sem_destroy() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html# + * + * It is safe to destroy a semaphore upon which no threads are currently blocked. The effect of destroying a + * semaphore upon which other threads are currently blocked is system error. + */ + + ~Semaphore() + { + + } + + /** + * \brief Gets current value of semaphore. + * + * Similar to sem_getvalue() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html# + * + * \return current value of semaphore, positive value if semaphore is not locked, zero otherwise + */ + + Value getValue() const + { + return value_; + } + + /** + * \brief Unlocks the semaphore. + * + * Similar to sem_post() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_post.html# + * + * This function shall unlock the semaphore by performing a semaphore unlock operation. If the semaphore value + * resulting from this operation is positive, then no threads were blocked waiting for the semaphore to become + * unlocked; the semaphore value is simply incremented. Otherwise one of the threads blocked waiting for the + * semaphore shall be allowed to return successfully from its call to lock() - the highest priority waiting thread + * shall be unblocked, and if there is more than one highest priority thread blocked waiting for the semaphore, then + * the highest priority thread that has been waiting the longest shall be unblocked. + * + * \return zero if the calling process successfully "posted" the semaphore, error code otherwise: + * - EOVERFLOW - the maximum allowable value for a semaphore would be exceeded; + */ + + int post(); + + /** + * \brief Tries to lock the semaphore. + * + * Similar to sem_trywait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html# + * + * This function shall lock the semaphore only if the semaphore is currently not locked; that is, if the semaphore + * value is currently positive. Otherwise, it shall not lock the semaphore. Upon successful return, the state of the + * semaphore shall be locked and shall remain locked until unlock() function is executed. + * + * \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise: + * - EAGAIN - semaphore was already locked, so it cannot be immediately locked by the tryWait() operation; + */ + + int tryWait(); + + /** + * \brief Tries to lock the semaphore for given duration of time. + * + * Similar to sem_timedwait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html# + * + * If the semaphore is already locked, the calling thread shall block until the semaphore becomes available as in + * wait() function. If the semaphore cannot be locked without waiting for another thread to unlock the semaphore, + * this wait shall be terminated when the specified timeout expires. + * + * Under no circumstance shall the function fail with a timeout if the semaphore can be locked immediately. The + * validity of the duration parameter need not be checked if the semaphore can be locked immediately. + * + * \param [in] duration is the duration after which the wait will be terminated without locking the semaphore + * + * \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ETIMEDOUT - the semaphore could not be locked before the specified timeout expired; + */ + + int tryWaitFor(TickClock::duration duration); + + /** + * \brief Tries to lock the semaphore for given duration of time. + * + * Template variant of tryWaitFor(TickClock::duration duration). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the wait will be terminated without locking the semaphore + * + * \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ETIMEDOUT - the semaphore could not be locked before the specified timeout expired; + */ + + template<typename Rep, typename Period> + int tryWaitFor(const std::chrono::duration<Rep, Period> duration) + { + return tryWaitFor(std::chrono::duration_cast<TickClock::duration>(duration)); + } + + /** + * \brief Tries to lock the semaphore until given time point. + * + * Similar to sem_timedwait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html# + * + * If the semaphore is already locked, the calling thread shall block until the semaphore becomes available as in + * wait() function. If the semaphore cannot be locked without waiting for another thread to unlock the semaphore, + * this wait shall be terminated when the specified timeout expires. + * + * Under no circumstance shall the function fail with a timeout if the semaphore can be locked immediately. The + * validity of the timePoint parameter need not be checked if the semaphore can be locked immediately. + * + * \param [in] timePoint is the time point at which the wait will be terminated without locking the semaphore + * + * \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ETIMEDOUT - the semaphore could not be locked before the specified timeout expired; + */ + + int tryWaitUntil(TickClock::time_point timePoint); + + /** + * \brief Tries to lock the semaphore until given time point. + * + * Template variant of tryWaitUntil(TickClock::time_point timePoint). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the wait will be terminated without locking the semaphore + * + * \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ETIMEDOUT - the semaphore could not be locked before the specified timeout expired; + */ + + template<typename Duration> + int tryWaitUntil(const std::chrono::time_point<TickClock, Duration> timePoint) + { + return tryWaitUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint)); + } + + /** + * \brief Locks the semaphore. + * + * Similar to sem_wait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html# + * + * This function shall lock the semaphore by performing a semaphore lock operation on that semaphore. If the + * semaphore value is currently zero, then the calling thread shall not return from the call to lock() until it + * either locks the semaphore or the call is interrupted by a signal. Upon successful return, the state of the + * semaphore shall be locked and shall remain locked until unlock() function is executed. + * + * \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + */ + + int wait(); + + Semaphore(const Semaphore&) = delete; + Semaphore(Semaphore&&) = default; + const Semaphore& operator=(const Semaphore&) = delete; + Semaphore& operator=(Semaphore&&) = delete; + +private: + + /** + * \brief Internal version of tryWait(). + * + * Internal version with no interrupt masking. + * + * \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise: + * - EAGAIN - semaphore was already locked, so it cannot be immediately locked by the tryWait() operation; + */ + + int tryWaitInternal(); + + /// ThreadControlBlock objects blocked on this semaphore + internal::ThreadList blockedList_; + + /// internal value of the semaphore + Value value_; + + /// max value of the semaphore + Value maxValue_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SEMAPHORE_HPP_ diff --git a/include/distortos/SignalAction.hpp b/include/distortos/SignalAction.hpp new file mode 100644 index 0000000..d91d696 --- /dev/null +++ b/include/distortos/SignalAction.hpp @@ -0,0 +1,96 @@ +/** + * \file + * \brief SignalAction class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SIGNALACTION_HPP_ +#define INCLUDE_DISTORTOS_SIGNALACTION_HPP_ + +#include "distortos/SignalSet.hpp" + +namespace distortos +{ + +class SignalInformation; + +/** + * \brief SignalAction class contains information needed to handle signal that was caught + * + * Similar to \a sigaction - http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html + * + * \ingroup signals + */ + +class SignalAction +{ +public: + + /// handler function + using Handler = void(const SignalInformation&); + + /** + * \brief SignalAction's constructor which uses "default" signal handler. + */ + + constexpr SignalAction() : + signalMask_{SignalSet::empty}, + handler_{} + { + + } + + /** + * \brief SignalAction's constructor. + * + * \param [in] handler is a reference to handler function (similar to \a sa_sigaction member of \a sigaction) + * \param [in] signalMask is the additional set of signals to be masked during execution of signal-catching function + * (similar to \a sa_mask member of \a sigaction) + */ + + constexpr SignalAction(Handler& handler, const SignalSet signalMask) : + signalMask_{signalMask}, + handler_{&handler} + { + + } + + /** + * \return pointer to handler function (similar to \a sa_sigaction member of \a sigaction), nullptr if use of + * default handler was configured (similar to \a SIG_DFL) + */ + + Handler* getHandler() const + { + return handler_; + } + + /** + * \return additional set of signals to be masked during execution of signal-catching function (similar to + * \a sa_mask member of \a sigaction) + */ + + SignalSet getSignalMask() const + { + return signalMask_; + } + +private: + + /// additional set of signals to be masked during execution of signal-catching function (similar to \a sa_mask + /// member of \a sigaction) + SignalSet signalMask_; + + /// pointer to handler function (similar to \a sa_sigaction member of \a sigaction), nullptr to use default handler + /// (similar to \a SIG_DFL) + Handler* handler_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SIGNALACTION_HPP_ diff --git a/include/distortos/SignalInformation.hpp b/include/distortos/SignalInformation.hpp new file mode 100644 index 0000000..b3d897c --- /dev/null +++ b/include/distortos/SignalInformation.hpp @@ -0,0 +1,100 @@ +/** + * \file + * \brief SignalInformation class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SIGNALINFORMATION_HPP_ +#define INCLUDE_DISTORTOS_SIGNALINFORMATION_HPP_ + +#include <csignal> +#include <cstdint> + +namespace distortos +{ + +/** + * \brief SignalInformation class contains information about queued signal + * + * Similar to siginfo_t - http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html + * + * \ingroup signals + */ + +class SignalInformation +{ +public: + + /// replacement for predefined \a si_code values + enum class Code : uint8_t + { + /// signal generated by Thread::generateSignal() or ThisThread::Signals::generateSignal() (similar to + /// \a SI_USER) + generated, + /// signal queued by Thread::queueSignal() or ThisThread::Signals::queueSignal() (similar to \a SI_QUEUE) + queued, + }; + + /** + * \brief SignalInformation's constructor. + * + * \param [in] signalNumber is the signal number (similar to \a si_signo member of \a siginfo_t) + * \param [in] code is the signal code (similar to \a si_code member of \a siginfo_t) + * \param [in] value is the signal value (similar to \a si_value member of \a siginfo_t) + */ + + constexpr SignalInformation(const uint8_t signalNumber, const Code code, const sigval value) : + value_(value), + code_{code}, + signalNumber_{signalNumber} + { + + } + + /** + * \return signal code (similar to \a si_code member of \a siginfo_t) + */ + + Code getCode() const + { + return code_; + } + + /** + * \return signal number (similar to \a si_signo member of \a siginfo_t) + */ + + uint8_t getSignalNumber() const + { + return signalNumber_; + } + + /** + * \return signal value (similar to \a si_value member of \a siginfo_t) + */ + + sigval getValue() const + { + return value_; + } + +private: + + /// signal value (similar to \a si_value member of \a siginfo_t) + sigval value_; + + /// signal code (similar to \a si_code member of \a siginfo_t) + Code code_; + + /// signal number (similar to \a si_signo member of \a siginfo_t) + uint8_t signalNumber_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SIGNALINFORMATION_HPP_ diff --git a/include/distortos/SignalInformationQueueWrapper.hpp b/include/distortos/SignalInformationQueueWrapper.hpp new file mode 100644 index 0000000..f3825a5 --- /dev/null +++ b/include/distortos/SignalInformationQueueWrapper.hpp @@ -0,0 +1,62 @@ +/** + * \file + * \brief SignalInformationQueueWrapper class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SIGNALINFORMATIONQUEUEWRAPPER_HPP_ +#define INCLUDE_DISTORTOS_SIGNALINFORMATIONQUEUEWRAPPER_HPP_ + +#include "distortos/internal/synchronization/SignalInformationQueue.hpp" + +namespace distortos +{ + +namespace internal +{ + +class SignalsReceiverControlBlock; + +} // namespace internal + +/// SignalInformationQueueWrapper class is a container for internal::SignalInformationQueue +class SignalInformationQueueWrapper +{ + friend class internal::SignalsReceiverControlBlock; + +public: + + /// import Storage type alias from internal::SignalInformationQueue + using Storage = internal::SignalInformationQueue::Storage; + + /// import StorageUniquePointer type alias from internal::SignalInformationQueue + using StorageUniquePointer = internal::SignalInformationQueue::StorageUniquePointer; + + /** + * \brief SignalInformationQueueWrapper's constructor + * + * \param [in] storageUniquePointer is a rvalue reference to StorageUniquePointer with storage for queue elements + * (sufficiently large for \a maxElements Storage objects) and appropriate deleter + * \param [in] maxElements is the number of elements in \a storage array + */ + + SignalInformationQueueWrapper(StorageUniquePointer&& storageUniquePointer, const size_t maxElements) : + signalInformationQueue_{std::move(storageUniquePointer), maxElements} + { + + } + +private: + + /// contained internal::SignalInformationQueue object + internal::SignalInformationQueue signalInformationQueue_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SIGNALINFORMATIONQUEUEWRAPPER_HPP_ diff --git a/include/distortos/SignalSet.hpp b/include/distortos/SignalSet.hpp new file mode 100644 index 0000000..8473bdd --- /dev/null +++ b/include/distortos/SignalSet.hpp @@ -0,0 +1,179 @@ +/** + * \file + * \brief SignalSet class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SIGNALSET_HPP_ +#define INCLUDE_DISTORTOS_SIGNALSET_HPP_ + +#include <bitset> + +namespace distortos +{ + +/** + * \brief SignalSet class is used as a set of signals. + * + * Similar to POSIX sigset_t - http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html + * + * \ingroup signals + */ + +class SignalSet +{ +public: + + /// type of internal bitset for 32 signals + using Bitset = std::bitset<32>; + + /// tag struct to construct empty SignalSet + struct Empty + { + + }; + + /// tag struct to construct full SignalSet + struct Full + { + + }; + + /// tag object to construct empty SignalSet + constexpr static Empty empty = {}; + + /// tag object to construct full SignalSet + constexpr static Full full = {}; + + /** + * \brief SignalSet's constructor + * + * \param [in] bitmask is the bit mask used to initialize internal bitset + */ + + constexpr explicit SignalSet(uint32_t bitmask) : + bitset_{bitmask} + { + + } + + /** + * \brief SignalSet's constructor + * + * \param [in] bitset is a reference to Bitset from which internal bitset is copy-constructed + */ + + constexpr explicit SignalSet(const Bitset& bitset) : + bitset_{bitset} + { + + } + + /** + * \brief SignalSet's constructor + * + * Constructs empty SignalSet. + * + * Similar to sigemptyset() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigemptyset.html# + */ + + constexpr explicit SignalSet(const Empty&) : + SignalSet{uint32_t{}} + { + + } + + /** + * \brief SignalSet's constructor + * + * Constructs full SignalSet. + * + * Similar to sigfillset() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigfillset.html# + */ + + constexpr explicit SignalSet(const Full&) : + SignalSet{~uint32_t{}} + { + + } + + /** + * \brief Sets single bit. + * + * Similar to sigaddset() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaddset.html# + * + * \param [in] signalNumber is the bit position that will be set, [0; 31] + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + */ + + int add(const uint8_t signalNumber) + { + return set(signalNumber, true); + } + + /** + * \return copy of internal bitset + */ + + Bitset getBitset() const + { + return bitset_; + } + + /** + * \brief Clears single bit. + * + * Similar to sigdelset() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigdelset.html# + * + * \param [in] signalNumber is the bit position that will be cleared, [0; 31] + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + */ + + int remove(const uint8_t signalNumber) + { + return set(signalNumber, false); + } + + /** + * \brief Tests whether the bit is set. + * + * Similar to sigismember() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigismember.html# + * + * \param [in] signalNumber is the bit position that will be tested, [0; 31] + * + * \return pair with return code (0 on success, error code otherwise) and value of selected bit; error codes: + * - EINVAL - \a signalNumber value is invalid; + */ + + std::pair<int, bool> test(uint8_t signalNumber) const; + +private: + + /** + * \brief Sets value of single bit. + * + * \param [in] signalNumber is the bit position that will be modified, [0; 31] + * \param [in] value is the new value for selected bit + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + */ + + int set(uint8_t signalNumber, bool value); + + /// internal bitset for 32 signals + Bitset bitset_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SIGNALSET_HPP_ diff --git a/include/distortos/SignalsCatcher.hpp b/include/distortos/SignalsCatcher.hpp new file mode 100644 index 0000000..effaf73 --- /dev/null +++ b/include/distortos/SignalsCatcher.hpp @@ -0,0 +1,62 @@ +/** + * \file + * \brief SignalsCatcher class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SIGNALSCATCHER_HPP_ +#define INCLUDE_DISTORTOS_SIGNALSCATCHER_HPP_ + +#include "distortos/internal/synchronization/SignalsCatcherControlBlock.hpp" + +namespace distortos +{ + +namespace internal +{ + +class SignalsReceiverControlBlock; + +} // namespace internal + +/// SignalsCatcher class is a container for internal::SignalsCatcherControlBlock +class SignalsCatcher +{ + friend class internal::SignalsReceiverControlBlock; + +public: + + /// import Storage type alias from internal::SignalsCatcherControlBlock + using Storage = internal::SignalsCatcherControlBlock::Storage; + + /// import StorageUniquePointer type alias from internal::SignalsCatcherControlBlock + using StorageUniquePointer = internal::SignalsCatcherControlBlock::StorageUniquePointer; + + /** + * \brief SignalsCatcher's constructor + * + * \param [in] storageUniquePointer is a rvalue reference to StorageUniquePointer with storage for + * internal::Association objects (sufficiently large for \a storageSize elements) and appropriate deleter + * \param [in] storageSize is the number of elements in \a storage array + */ + + SignalsCatcher(StorageUniquePointer&& storageUniquePointer, const size_t storageSize) : + signalsCatcherControlBlock_{std::move(storageUniquePointer), storageSize} + { + + } + +private: + + /// contained internal::SignalsCatcherControlBlock object + internal::SignalsCatcherControlBlock signalsCatcherControlBlock_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SIGNALSCATCHER_HPP_ diff --git a/include/distortos/SignalsReceiver.hpp b/include/distortos/SignalsReceiver.hpp new file mode 100644 index 0000000..fa41cad --- /dev/null +++ b/include/distortos/SignalsReceiver.hpp @@ -0,0 +1,58 @@ +/** + * \file + * \brief SignalsReceiver class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SIGNALSRECEIVER_HPP_ +#define INCLUDE_DISTORTOS_SIGNALSRECEIVER_HPP_ + +#include "distortos/internal/synchronization/SignalsReceiverControlBlock.hpp" + +namespace distortos +{ + +namespace internal +{ + +class ThreadControlBlock; + +} // namespace internal + +/// SignalsReceiver class is a container for internal::SignalsReceiverControlBlock +class SignalsReceiver +{ + friend class internal::ThreadControlBlock; + +public: + + /** + * \brief SignalsReceiver's constructor + * + * \param [in] signalInformationQueueWrapper is a pointer to SignalInformationQueueWrapper for this receiver, + * nullptr to disable queuing of signals for this receiver + * \param [in] signalsCatcher is a pointer to SignalsCatcher for this receiver, nullptr if this receiver cannot + * catch/handle signals + */ + + explicit SignalsReceiver(SignalInformationQueueWrapper* const signalInformationQueueWrapper, + SignalsCatcher* const signalsCatcher) : + signalsReceiverControlBlock_{signalInformationQueueWrapper, signalsCatcher} + { + + } + +private: + + /// contained internal::SignalsReceiverControlBlock object + internal::SignalsReceiverControlBlock signalsReceiverControlBlock_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SIGNALSRECEIVER_HPP_ diff --git a/include/distortos/SoftwareTimer.hpp b/include/distortos/SoftwareTimer.hpp new file mode 100644 index 0000000..c10060c --- /dev/null +++ b/include/distortos/SoftwareTimer.hpp @@ -0,0 +1,130 @@ +/** + * \file + * \brief SoftwareTimer class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SOFTWARETIMER_HPP_ +#define INCLUDE_DISTORTOS_SOFTWARETIMER_HPP_ + +#include "distortos/TickClock.hpp" + +namespace distortos +{ + +/** + * \brief SoftwareTimer class is an abstract interface for software timers + * + * \ingroup softwareTimers + */ + +class SoftwareTimer +{ +public: + + /** + * \brief SoftwareTimer's destructor + */ + + virtual ~SoftwareTimer() = 0; + + /** + * \return true if the timer is running, false otherwise + */ + + virtual bool isRunning() const = 0; + + /** + * \brief Starts the timer. + * + * \note The duration will never be shorter, so one additional tick is always added to the duration. + * + * \param [in] duration is the duration after which the function will be executed + * + * \return 0 on success, error code otherwise + */ + + int start(TickClock::duration duration); + + /** + * \brief Starts the timer. + * + * \note The duration must not be shorter, so one additional tick is always added to the duration. + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the function will be executed + * + * \return 0 on success, error code otherwise + */ + + template<typename Rep, typename Period> + int start(const std::chrono::duration<Rep, Period> duration) + { + return start(std::chrono::duration_cast<TickClock::duration>(duration)); + } + + /** + * \brief Starts the timer. + * + * \param [in] timePoint is the time point at which the function will be executed + * + * \return 0 on success, error code otherwise + */ + + virtual int start(TickClock::time_point timePoint) = 0; + + /** + * \brief Starts the timer. + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the function will be executed + * + * \return 0 on success, error code otherwise + */ + + template<typename Duration> + int start(const std::chrono::time_point<TickClock, Duration> timePoint) + { + return start(std::chrono::time_point_cast<TickClock::duration>(timePoint)); + } + + /** + * \brief Stops the timer. + * + * \return 0 on success, error code otherwise + */ + + virtual int stop() = 0; + +protected: + + /** + * \brief Software timer's function runner + * + * \param [in] softwareTimer is a reference to SoftwareTimer object that is being run + */ + + static void softwareTimerRunner(SoftwareTimer& softwareTimer); + +private: + + /** + * \brief "Run" function of software timer + * + * This should be overridden by derived classes. + */ + + virtual void run() = 0; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SOFTWARETIMER_HPP_ diff --git a/include/distortos/SoftwareTimerCommon.hpp b/include/distortos/SoftwareTimerCommon.hpp new file mode 100644 index 0000000..636a264 --- /dev/null +++ b/include/distortos/SoftwareTimerCommon.hpp @@ -0,0 +1,87 @@ +/** + * \file + * \brief SoftwareTimerCommon class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_SOFTWARETIMERCOMMON_HPP_ +#define INCLUDE_DISTORTOS_SOFTWARETIMERCOMMON_HPP_ + +#include "distortos/SoftwareTimer.hpp" + +#include "distortos/internal/scheduler/SoftwareTimerControlBlock.hpp" + +namespace distortos +{ + +/** + * \brief SoftwareTimerCommon class implements common functionality of software timers + * + * \ingroup softwareTimers + */ + +class SoftwareTimerCommon : public SoftwareTimer +{ +public: + + /** + * \brief SoftwareTimerCommon's constructor + */ + + constexpr SoftwareTimerCommon() : + softwareTimerControlBlock_{softwareTimerRunner, *this} + { + + } + + /** + * \brief SoftwareTimerCommon's destructor + */ + + ~SoftwareTimerCommon() override; + + /** + * \return true if the timer is running, false otherwise + */ + + bool isRunning() const override; + + /** + * \brief Starts the timer. + * + * \param [in] timePoint is the time point at which the function will be executed + * + * \return 0 on success, error code otherwise + */ + + int start(TickClock::time_point timePoint) override; + + using SoftwareTimer::start; + + /** + * \brief Stops the timer. + * + * \return 0 on success, error code otherwise + */ + + int stop() override; + + SoftwareTimerCommon(const SoftwareTimerCommon&) = delete; + SoftwareTimerCommon(SoftwareTimerCommon&&) = default; + const SoftwareTimerCommon& operator=(const SoftwareTimerCommon&) = delete; + SoftwareTimerCommon& operator=(SoftwareTimerCommon&&) = delete; + +private: + + /// internal SoftwareTimerControlBlock object + internal::SoftwareTimerControlBlock softwareTimerControlBlock_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SOFTWARETIMERCOMMON_HPP_ diff --git a/include/distortos/StaticFifoQueue.hpp b/include/distortos/StaticFifoQueue.hpp new file mode 100644 index 0000000..63bb636 --- /dev/null +++ b/include/distortos/StaticFifoQueue.hpp @@ -0,0 +1,57 @@ +/** + * \file + * \brief StaticFifoQueue class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_STATICFIFOQUEUE_HPP_ +#define INCLUDE_DISTORTOS_STATICFIFOQUEUE_HPP_ + +#include "FifoQueue.hpp" + +#include "distortos/internal/memory/dummyDeleter.hpp" + +namespace distortos +{ + +/** + * \brief StaticFifoQueue class is a variant of FifoQueue that has automatic storage for queue's contents. + * + * \tparam T is the type of data in queue + * \tparam QueueSize is the maximum number of elements in queue + * + * \ingroup queues + */ + +template<typename T, size_t QueueSize> +class StaticFifoQueue : public FifoQueue<T> +{ +public: + + /// import Storage type from base class + using typename FifoQueue<T>::Storage; + + /** + * \brief StaticFifoQueue's constructor + */ + + explicit StaticFifoQueue() : + FifoQueue<T>{{storage_.data(), internal::dummyDeleter<Storage>}, storage_.size()} + { + + } + +private: + + /// storage for queue's contents + std::array<Storage, QueueSize> storage_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_STATICFIFOQUEUE_HPP_ diff --git a/include/distortos/StaticMessageQueue.hpp b/include/distortos/StaticMessageQueue.hpp new file mode 100644 index 0000000..ed370d0 --- /dev/null +++ b/include/distortos/StaticMessageQueue.hpp @@ -0,0 +1,64 @@ +/** + * \file + * \brief StaticMessageQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_STATICMESSAGEQUEUE_HPP_ +#define INCLUDE_DISTORTOS_STATICMESSAGEQUEUE_HPP_ + +#include "MessageQueue.hpp" + +#include "distortos/internal/memory/dummyDeleter.hpp" + +namespace distortos +{ + +/** + * \brief StaticMessageQueue class is a variant of MessageQueue that has automatic storage for queue's contents. + * + * \tparam T is the type of data in queue + * \tparam QueueSize is the maximum number of elements in queue + * + * \ingroup queues + */ + +template<typename T, size_t QueueSize> +class StaticMessageQueue : public MessageQueue<T> +{ +public: + + /// import EntryStorage type from base class + using typename MessageQueue<T>::EntryStorage; + + /// import ValueStorage type from base class + using typename MessageQueue<T>::ValueStorage; + + /** + * \brief StaticMessageQueue's constructor + */ + + explicit StaticMessageQueue() : + MessageQueue<T>{{entryStorage_.data(), internal::dummyDeleter<EntryStorage>}, + {valueStorage_.data(), internal::dummyDeleter<ValueStorage>}, valueStorage_.size()} + { + + } + +private: + + /// storage for queue's entries + std::array<EntryStorage, QueueSize> entryStorage_; + + /// storage for queue's contents + std::array<ValueStorage, QueueSize> valueStorage_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_STATICMESSAGEQUEUE_HPP_ diff --git a/include/distortos/StaticRawFifoQueue.hpp b/include/distortos/StaticRawFifoQueue.hpp new file mode 100644 index 0000000..a271020 --- /dev/null +++ b/include/distortos/StaticRawFifoQueue.hpp @@ -0,0 +1,69 @@ +/** + * \file + * \brief StaticRawFifoQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_STATICRAWFIFOQUEUE_HPP_ +#define INCLUDE_DISTORTOS_STATICRAWFIFOQUEUE_HPP_ + +#include "RawFifoQueue.hpp" + +#include "distortos/internal/memory/dummyDeleter.hpp" + +namespace distortos +{ + +/** + * \brief StaticRawFifoQueue class is a variant of RawFifoQueue that has automatic storage for queue's contents. + * + * \tparam T is the type of data in queue + * \tparam QueueSize is the maximum number of elements in queue + * + * \ingroup queues + */ + +template<typename T, size_t QueueSize> +class StaticRawFifoQueue : public RawFifoQueue +{ +public: + + /// type of uninitialized storage for data + using Storage = typename std::aligned_storage<sizeof(T), alignof(T)>::type; + + /** + * \brief StaticRawFifoQueue's constructor + */ + + explicit StaticRawFifoQueue() : + RawFifoQueue{{storage_.data(), internal::dummyDeleter<Storage>}, sizeof(*storage_.data()), storage_.size()} + { + + } + +private: + + /// storage for queue's contents + std::array<Storage, QueueSize> storage_; +}; + +/** + * \brief StaticRawFifoQueueFromSize type alias is a variant of StaticRawFifoQueue which uses size of element (instead + * of type) as template argument. + * + * \tparam ElementSize is the size of single queue element, bytes + * \tparam QueueSize is the maximum number of elements in queue + */ + +template<size_t ElementSize, size_t QueueSize> +using StaticRawFifoQueueFromSize = + StaticRawFifoQueue<typename std::aligned_storage<ElementSize, ElementSize>::type, QueueSize>; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_STATICRAWFIFOQUEUE_HPP_ diff --git a/include/distortos/StaticRawMessageQueue.hpp b/include/distortos/StaticRawMessageQueue.hpp new file mode 100644 index 0000000..8120d17 --- /dev/null +++ b/include/distortos/StaticRawMessageQueue.hpp @@ -0,0 +1,71 @@ +/** + * \file + * \brief StaticRawMessageQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_STATICRAWMESSAGEQUEUE_HPP_ +#define INCLUDE_DISTORTOS_STATICRAWMESSAGEQUEUE_HPP_ + +#include "distortos/RawMessageQueue.hpp" + +#include "distortos/internal/memory/dummyDeleter.hpp" + +namespace distortos +{ + +/** + * \brief StaticRawMessageQueue class is a variant of RawMessageQueue that has automatic storage for queue's contents. + * + * \tparam T is the type of data in queue + * \tparam QueueSize is the maximum number of elements in queue + * + * \ingroup queues + */ + +template<typename T, size_t QueueSize> +class StaticRawMessageQueue : public RawMessageQueue +{ +public: + + /** + * \brief StaticRawMessageQueue's constructor + */ + + explicit StaticRawMessageQueue() : + RawMessageQueue{{entryStorage_.data(), internal::dummyDeleter<EntryStorage>}, + {valueStorage_.data(), internal::dummyDeleter<ValueStorage<T>>}, sizeof(*valueStorage_.data()), + valueStorage_.size()} + { + + } + +private: + + /// storage for queue's entries + std::array<EntryStorage, QueueSize> entryStorage_; + + /// storage for queue's contents + std::array<ValueStorage<T>, QueueSize> valueStorage_; +}; + +/** + * \brief StaticRawMessageQueueFromSize type alias is a variant of StaticRawMessageQueue which uses size of element + * (instead of type) as template argument. + * + * \tparam ElementSize is the size of single queue element, bytes + * \tparam QueueSize is the maximum number of elements in queue + */ + +template<size_t ElementSize, size_t QueueSize> +using StaticRawMessageQueueFromSize = + StaticRawMessageQueue<typename std::aligned_storage<ElementSize, ElementSize>::type, QueueSize>; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_STATICRAWMESSAGEQUEUE_HPP_ diff --git a/include/distortos/StaticSignalsReceiver.hpp b/include/distortos/StaticSignalsReceiver.hpp new file mode 100644 index 0000000..a6894ab --- /dev/null +++ b/include/distortos/StaticSignalsReceiver.hpp @@ -0,0 +1,166 @@ +/** + * \file + * \brief StaticSignalsReceiver class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_STATICSIGNALSRECEIVER_HPP_ +#define INCLUDE_DISTORTOS_STATICSIGNALSRECEIVER_HPP_ + +#include "distortos/SignalInformationQueueWrapper.hpp" +#include "distortos/SignalsCatcher.hpp" +#include "distortos/SignalsReceiver.hpp" + +#include "distortos/internal/memory/dummyDeleter.hpp" + +namespace distortos +{ + +/** + * \brief StaticSignalsReceiver class is a templated interface for SignalsReceiver that has automatic storage for queued + * signals and SignalAction associations required for catching signals. + * + * \tparam QueuedSignals is the max number of queued signals, 0 to disable queuing of signals for this receiver + * \tparam SignalActions is the max number of different SignalAction objects, 0 to disable catching of signals for this + * receiver + */ + +template<size_t QueuedSignals, size_t SignalActions> +class StaticSignalsReceiver : public SignalsReceiver +{ +public: + + /** + * \brief StaticSignalsReceiver's constructor + */ + + StaticSignalsReceiver() : + SignalsReceiver{&signalInformationQueueWrapper_, &signalsCatcher_}, + signalInformationQueueWrapper_{{signalInformationQueueWrapperStorage_.data(), + internal::dummyDeleter<SignalInformationQueueWrapper::Storage>}, + signalInformationQueueWrapperStorage_.size()}, + signalsCatcher_{{signalsCatcherStorage_.data(), internal::dummyDeleter<SignalsCatcher::Storage>}, + signalsCatcherStorage_.size()} + { + + } + +private: + + /// storage for \a signalInformationQueueWrapper_ + std::array<SignalInformationQueueWrapper::Storage, QueuedSignals> signalInformationQueueWrapperStorage_; + + /// internal SignalInformationQueueWrapper object + SignalInformationQueueWrapper signalInformationQueueWrapper_; + + /// storage for \a signalsCatcher_ + std::array<SignalsCatcher::Storage, SignalActions> signalsCatcherStorage_; + + /// internal SignalsCatcher object + SignalsCatcher signalsCatcher_; +}; + +/** + * \brief StaticSignalsReceiver class is a templated interface for SignalsReceiver that has automatic storage for queued + * signals and SignalAction associations required for catching signals. + * + * \tparam QueuedSignals is the max number of queued signals + * + * Specialization for receiver with enabled queuing (QueuedSignals != 0) and disabled catching (SignalActions == 0) of + * signals + */ + +template<size_t QueuedSignals> +class StaticSignalsReceiver<QueuedSignals, 0> : public SignalsReceiver +{ +public: + + /** + * \brief StaticSignalsReceiver's constructor + */ + + StaticSignalsReceiver() : + SignalsReceiver{&signalInformationQueueWrapper_, nullptr}, + signalInformationQueueWrapper_{{signalInformationQueueWrapperStorage_.data(), + internal::dummyDeleter<SignalInformationQueueWrapper::Storage>}, + signalInformationQueueWrapperStorage_.size()} + { + + } + +private: + + /// storage for \a signalInformationQueueWrapper_ + std::array<SignalInformationQueueWrapper::Storage, QueuedSignals> signalInformationQueueWrapperStorage_; + + /// internal SignalInformationQueueWrapper object + SignalInformationQueueWrapper signalInformationQueueWrapper_; +}; + +/** + * \brief StaticSignalsReceiver class is a templated interface for SignalsReceiver that has automatic storage for queued + * signals and SignalAction associations required for catching signals. + * + * \tparam SignalActions is the max number of different SignalAction objects + * + * Specialization for receiver with disabled queuing (QueuedSignals == 0) and enabled catching (SignalActions != 0) of + * signals + */ + +template<size_t SignalActions> +class StaticSignalsReceiver<0, SignalActions> : public SignalsReceiver +{ +public: + + /** + * \brief StaticSignalsReceiver's constructor + */ + + StaticSignalsReceiver() : + SignalsReceiver{nullptr, &signalsCatcher_}, + signalsCatcher_{{signalsCatcherStorage_.data(), internal::dummyDeleter<SignalsCatcher::Storage>}, + signalsCatcherStorage_.size()} + { + + } + +private: + + /// storage for \a signalsCatcher_ + std::array<SignalsCatcher::Storage, SignalActions> signalsCatcherStorage_; + + /// internal SignalsCatcher object + SignalsCatcher signalsCatcher_; +}; + +/** + * \brief StaticSignalsReceiver class is a templated interface for SignalsReceiver that has automatic storage for queued + * signals and SignalAction associations required for catching signals. + * + * Specialization for receiver with disabled queuing (QueuedSignals == 0) and catching (SignalActions == 0) of signals + */ + +template<> +class StaticSignalsReceiver<0, 0> : public SignalsReceiver +{ +public: + + /** + * \brief StaticSignalsReceiver's constructor + */ + + StaticSignalsReceiver() : + SignalsReceiver{nullptr, nullptr} + { + + } +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_STATICSIGNALSRECEIVER_HPP_ diff --git a/include/distortos/StaticSoftwareTimer.hpp b/include/distortos/StaticSoftwareTimer.hpp new file mode 100644 index 0000000..e1c27f7 --- /dev/null +++ b/include/distortos/StaticSoftwareTimer.hpp @@ -0,0 +1,90 @@ +/** + * \file + * \brief StaticSoftwareTimer class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_STATICSOFTWARETIMER_HPP_ +#define INCLUDE_DISTORTOS_STATICSOFTWARETIMER_HPP_ + +#include "distortos/SoftwareTimerCommon.hpp" + +#include <functional> + +namespace distortos +{ + +/// \addtogroup softwareTimers +/// \{ + +/** + * \brief StaticSoftwareTimer class is a templated interface for software timer + * + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for function + */ + +template<typename Function, typename... Args> +class StaticSoftwareTimer : public SoftwareTimerCommon +{ +public: + + /** + * \brief StaticSoftwareTimer's constructor + * + * \param [in] function is a function that will be executed from interrupt context at a later time + * \param [in] args are arguments for function + */ + + StaticSoftwareTimer(Function&& function, Args&&... args) : + SoftwareTimerCommon{}, + boundFunction_{std::bind(std::forward<Function>(function), std::forward<Args>(args)...)} + { + + } + +private: + + /** + * \brief "Run" function of software timer + * + * Executes bound function object. + */ + + void run() override + { + boundFunction_(); + } + + /// bound function object + decltype(std::bind(std::declval<Function>(), std::declval<Args>()...)) boundFunction_; +}; + +/** + * \brief Helper factory function to make StaticSoftwareTimer object with deduced template arguments + * + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for function + * + * \param [in] function is a function that will be executed from interrupt context at a later time + * \param [in] args are arguments for function + * + * \return StaticSoftwareTimer object with deduced template arguments + */ + +template<typename Function, typename... Args> +StaticSoftwareTimer<Function, Args...> makeStaticSoftwareTimer(Function&& function, Args&&... args) +{ + return {std::forward<Function>(function), std::forward<Args>(args)...}; +} + +/// \} + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_STATICSOFTWARETIMER_HPP_ diff --git a/include/distortos/StaticThread.hpp b/include/distortos/StaticThread.hpp new file mode 100644 index 0000000..c135e55 --- /dev/null +++ b/include/distortos/StaticThread.hpp @@ -0,0 +1,357 @@ +/** + * \file + * \brief StaticThread class header + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_STATICTHREAD_HPP_ +#define INCLUDE_DISTORTOS_STATICTHREAD_HPP_ + +#include "distortos/StaticSignalsReceiver.hpp" +#include "distortos/UndetachableThread.hpp" + +namespace distortos +{ + +namespace internal +{ + +/** + * \brief StaticThreadBase class is a templated common base for StaticThread + * + * \tparam Function is the function that will be executed in separate thread + * \tparam Args are the arguments for \a Function + */ + +template<typename Function, typename... Args> +class StaticThreadBase : public UndetachableThread +{ +public: + + /// base of StaticThreadBase + using Base = UndetachableThread; + + /** + * \brief StaticThreadBase's constructor + * + * \param [in] stackStorageUniquePointer is a rvalue reference to StackStorageUniquePointer with storage for stack + * (\a size bytes long) and appropriate deleter + * \param [in] size is the size of stack's storage, bytes + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] signalsReceiver is a pointer to SignalsReceiver object for this thread, nullptr to disable reception + * of signals for this thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + */ + + StaticThreadBase(StackStorageUniquePointer&& stackStorageUniquePointer, const size_t size, const uint8_t priority, + const SchedulingPolicy schedulingPolicy, SignalsReceiver* const signalsReceiver, Function&& function, + Args&&... args) : + Base{{std::move(stackStorageUniquePointer), size, *this, run, nullptr, terminationHook}, + priority, schedulingPolicy, nullptr, signalsReceiver}, + boundFunction_{std::bind(std::forward<Function>(function), std::forward<Args>(args)...)} + { + + } + + StaticThreadBase(const StaticThreadBase&) = delete; + StaticThreadBase(StaticThreadBase&&) = default; + const StaticThreadBase& operator=(const StaticThreadBase&) = delete; + StaticThreadBase& operator=(StaticThreadBase&&) = delete; + +private: + + /** + * \brief Thread's "run" function. + * + * Executes bound function object. + * + * \param [in] thread is a reference to Thread object, this must be StaticThreadBase! + */ + + static void run(Thread& thread) + { + static_cast<StaticThreadBase&>(thread).boundFunction_(); + } + + /// bound function object + decltype(std::bind(std::declval<Function>(), std::declval<Args>()...)) boundFunction_; +}; + +} // namespace internal + +/// \addtogroup threads +/// \{ + +/** + * \brief StaticThread class is a templated interface for thread that has automatic storage for stack. + * + * \tparam StackSize is the size of stack, bytes + * \tparam CanReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this thread + * \tparam QueuedSignals is the max number of queued signals for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable queuing of signals for this thread + * \tparam SignalActions is the max number of different SignalAction objects for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable catching of signals for this thread + * \tparam Function is the function that will be executed in separate thread + * \tparam Args are the arguments for \a Function + */ + +template<size_t StackSize, bool CanReceiveSignals, size_t QueuedSignals, size_t SignalActions, typename Function, + typename... Args> +class StaticThread : public internal::StaticThreadBase<Function, Args...> +{ +public: + + /// base of StaticThread + using Base = internal::StaticThreadBase<Function, Args...>; + + /** + * \brief StaticThread's constructor + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + */ + + StaticThread(uint8_t priority, SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args); + + /** + * \brief StaticThread's constructor + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + */ + + StaticThread(const uint8_t priority, Function&& function, Args&&... args) : + StaticThread{priority, SchedulingPolicy::roundRobin, std::forward<Function>(function), + std::forward<Args>(args)...} + { + + } + + StaticThread(const StaticThread&) = delete; + StaticThread(StaticThread&&) = default; + const StaticThread& operator=(const StaticThread&) = delete; + StaticThread& operator=(StaticThread&&) = delete; + +private: + + /// stack buffer + typename std::aligned_storage<StackSize>::type stack_; +}; + +/** + * \brief StaticThread class is a templated interface for thread that has automatic storage for stack and internal + * StaticSignalsReceiver object. + * + * Specialization for threads with enabled reception of signals (CanReceiveSignals == true) + * + * \tparam StackSize is the size of stack, bytes + * \tparam QueuedSignals is the max number of queued signals for this thread, 0 to disable queuing of signals for this + * thread + * \tparam SignalActions is the max number of different SignalAction objects for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable catching of signals for this thread + * \tparam Function is the function that will be executed in separate thread + * \tparam Args are the arguments for \a Function + */ + +template<size_t StackSize, size_t QueuedSignals, size_t SignalActions, typename Function, typename... Args> +class StaticThread<StackSize, true, QueuedSignals, SignalActions, Function, Args...> : + public internal::StaticThreadBase<Function, Args...> +{ +public: + + /// base of StaticThread + using Base = internal::StaticThreadBase<Function, Args...>; + + /** + * \brief StaticThread's constructor + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + */ + + StaticThread(uint8_t priority, SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args); + + /** + * \brief StaticThread's constructor + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + */ + + StaticThread(const uint8_t priority, Function&& function, Args&&... args) : + StaticThread{priority, SchedulingPolicy::roundRobin, std::forward<Function>(function), + std::forward<Args>(args)...} + { + + } + + StaticThread(const StaticThread&) = delete; + StaticThread(StaticThread&&) = default; + const StaticThread& operator=(const StaticThread&) = delete; + StaticThread& operator=(StaticThread&&) = delete; + +private: + + /// stack buffer + typename std::aligned_storage<StackSize>::type stack_; + + /// internal StaticSignalsReceiver object + StaticSignalsReceiver<QueuedSignals, SignalActions> staticSignalsReceiver_; +}; + +/** + * \brief Helper factory function to make StaticThread object with partially deduced template arguments + * + * \tparam StackSize is the size of stack, bytes + * \tparam CanReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this thread + * \tparam QueuedSignals is the max number of queued signals for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable queuing of signals for this thread + * \tparam SignalActions is the max number of different SignalAction objects for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable catching of signals for this thread + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + * + * \return StaticThread object with partially deduced template arguments + */ + +template<size_t StackSize, bool CanReceiveSignals = {}, size_t QueuedSignals = {}, size_t SignalActions = {}, + typename Function, typename... Args> +StaticThread<StackSize, CanReceiveSignals, QueuedSignals, SignalActions, Function, Args...> +makeStaticThread(const uint8_t priority, const SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args) +{ + return {priority, schedulingPolicy, std::forward<Function>(function), std::forward<Args>(args)...}; +} + +/** + * \brief Helper factory function to make StaticThread object with partially deduced template arguments + * + * \tparam StackSize is the size of stack, bytes + * \tparam CanReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this thread + * \tparam QueuedSignals is the max number of queued signals for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable queuing of signals for this thread + * \tparam SignalActions is the max number of different SignalAction objects for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable catching of signals for this thread + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + * + * \return StaticThread object with partially deduced template arguments + */ + +template<size_t StackSize, bool CanReceiveSignals = {}, size_t QueuedSignals = {}, size_t SignalActions = {}, + typename Function, typename... Args> +StaticThread<StackSize, CanReceiveSignals, QueuedSignals, SignalActions, Function, Args...> +makeStaticThread(const uint8_t priority, Function&& function, Args&&... args) +{ + return {priority, std::forward<Function>(function), std::forward<Args>(args)...}; +} + +/** + * \brief Helper factory function to make and start StaticThread object with partially deduced template arguments + * + * \tparam StackSize is the size of stack, bytes + * \tparam CanReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this thread + * \tparam QueuedSignals is the max number of queued signals for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable queuing of signals for this thread + * \tparam SignalActions is the max number of different SignalAction objects for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable catching of signals for this thread + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + * + * \return StaticThread object with partially deduced template arguments + */ + +template<size_t StackSize, bool CanReceiveSignals = {}, size_t QueuedSignals = {}, size_t SignalActions = {}, + typename Function, typename... Args> +StaticThread<StackSize, CanReceiveSignals, QueuedSignals, SignalActions, Function, Args...> +makeAndStartStaticThread(const uint8_t priority, const SchedulingPolicy schedulingPolicy, Function&& function, + Args&&... args) +{ + auto thread = makeStaticThread<StackSize, CanReceiveSignals, QueuedSignals, SignalActions>(priority, + schedulingPolicy, std::forward<Function>(function), std::forward<Args>(args)...); + thread.start(); /// \todo make sure this never fails + return thread; +} + +/** + * \brief Helper factory function to make and start StaticThread object with partially deduced template arguments + * + * \tparam StackSize is the size of stack, bytes + * \tparam CanReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this thread + * \tparam QueuedSignals is the max number of queued signals for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable queuing of signals for this thread + * \tparam SignalActions is the max number of different SignalAction objects for this thread, relevant only if + * CanReceiveSignals == true, 0 to disable catching of signals for this thread + * \tparam Function is the function that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for function + * + * \return StaticThread object with partially deduced template arguments + */ + +template<size_t StackSize, bool CanReceiveSignals = {}, size_t QueuedSignals = {}, size_t SignalActions = {}, + typename Function, typename... Args> +StaticThread<StackSize, CanReceiveSignals, QueuedSignals, SignalActions, Function, Args...> +makeAndStartStaticThread(const uint8_t priority, Function&& function, Args&&... args) +{ + auto thread = makeStaticThread<StackSize, CanReceiveSignals, QueuedSignals, SignalActions>(priority, + std::forward<Function>(function), std::forward<Args>(args)...); + thread.start(); /// \todo make sure this never fails + return thread; +} + +/// \} + +template<size_t StackSize, bool CanReceiveSignals, size_t QueuedSignals, size_t SignalActions, typename Function, + typename... Args> +StaticThread<StackSize, CanReceiveSignals, QueuedSignals, SignalActions, Function, Args...>:: +StaticThread(const uint8_t priority, const SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args) : + Base{{&stack_, internal::dummyDeleter<decltype(stack_)>}, sizeof(stack_), priority, schedulingPolicy, nullptr, + std::forward<Function>(function), std::forward<Args>(args)...} +{ + +} + +template<size_t StackSize, size_t QueuedSignals, size_t SignalActions, typename Function, typename... Args> +StaticThread<StackSize, true, QueuedSignals, SignalActions, Function, Args...>::StaticThread(const uint8_t priority, + const SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args) : + Base{{&stack_, internal::dummyDeleter<decltype(stack_)>}, sizeof(stack_), priority, schedulingPolicy, + &static_cast<SignalsReceiver&>(staticSignalsReceiver_), std::forward<Function>(function), + std::forward<Args>(args)...}, + staticSignalsReceiver_{} +{ + +} + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_STATICTHREAD_HPP_ diff --git a/include/distortos/ThisThread-Signals.hpp b/include/distortos/ThisThread-Signals.hpp new file mode 100644 index 0000000..b0a808d --- /dev/null +++ b/include/distortos/ThisThread-Signals.hpp @@ -0,0 +1,279 @@ +/** + * \file + * \brief ThisThread::Signals namespace header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_THISTHREAD_SIGNALS_HPP_ +#define INCLUDE_DISTORTOS_THISTHREAD_SIGNALS_HPP_ + +#include "distortos/SignalInformation.hpp" +#include "distortos/TickClock.hpp" + +#include <utility> + +#include <cstdint> + +namespace distortos +{ + +class SignalAction; +class SignalSet; + +namespace ThisThread +{ + +namespace Signals +{ + +/// \addtogroup signals +/// \{ + +/** + * \brief Generates signal for current thread. + * + * Similar to raise() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html + * + * Adds the signalNumber to set of pending signals of current thread. + * + * \param [in] signalNumber is the signal that will be generated, [0; 31] + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception of signals is disabled for current thread; + */ + +int generateSignal(uint8_t signalNumber); + +/** + * \brief Gets set of currently pending signals for current thread. + * + * Similar to sigpending() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigpending.html + * + * This function shall return the set of signals that are blocked from delivery and are pending on the current thread. + * + * \return set of currently pending signals for current thread + */ + +SignalSet getPendingSignalSet(); + +/** + * \brief Gets SignalAction associated with given signal number. + * + * Similar to sigaction() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html + * + * \param [in] signalNumber is the signal for which the association is requested, [0; 31] + * + * \return pair with return code (0 on success, error code otherwise) and SignalAction that is associated with + * \a signalNumber, default-constructed object if no association was found; + * error codes: + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception or catching/handling of signals are disabled for current thread; + */ + +std::pair<int, SignalAction> getSignalAction(uint8_t signalNumber); + +/** + * \brief Gets signal mask for current thread. + * + * Similar to pthread_sigmask() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html# + * + * \return SignalSet with signal mask for current thread + */ + +SignalSet getSignalMask(); + +/** + * \brief Queues signal for current thread. + * + * Similar to sigqueue() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigqueue.html + * + * Adds the signalNumber and signal value (sigval union) to queue of SignalInformation objects. + * + * \param [in] signalNumber is the signal that will be queued, [0; 31] + * \param [in] value is the signal value + * + * \return 0 on success, error code otherwise: + * - EAGAIN - no resources are available to queue the signal, maximal number of signals is already queued in + * associated queue of SignalInformation objects; + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception or queuing of signals are disabled for current thread; + */ + +int queueSignal(uint8_t signalNumber, sigval value); + +/** + * \brief Sets association for given signal number. + * + * Similar to sigaction() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html + * + * \param [in] signalNumber is the signal for which the association will be set, [0; 31] + * \param [in] signalAction is a reference to SignalAction that will be associated with given signal number, object in + * internal storage is copy-constructed + * + * \return pair with return code (0 on success, error code otherwise) and SignalAction that was associated with + * \a signalNumber, default-constructed object if no association was found; + * error codes: + * - EAGAIN - no resources are available to associate \a signalNumber with \a signalAction; + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception or catching/handling of signals are disabled for current thread; + */ + +std::pair<int, SignalAction> setSignalAction(uint8_t signalNumber, const SignalAction& signalAction); + +/** + * \brief Sets signal mask for current thread. + * + * Similar to pthread_sigmask() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html# + * + * \param [in] signalMask is the SignalSet with new signal mask for current thread + * + * \return 0 on success, error code otherwise: + * - ENOTSUP - reception or catching/handling of signals are disabled for current thread; + */ + +int setSignalMask(SignalSet signalMask); + +/** + * \brief Tries to accept pending signals. + * + * Similar to sigtimedwait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html + * + * This function shall select the lowest pending signal from provided set, atomically clear it from current thread's set + * of pending signals and return that signal number. If no signal in provided set is pending at the time of the call, + * then this function shall return immediately with an error. + * + * \param [in] signalSet is a reference to set of signals that may be accepted + * + * \return pair with return code (0 on success, error code otherwise) and SignalInformation object for accepted signal; + * error codes: + * - EAGAIN - no signal specified by \a signalSet was pending; + * - ENOTSUP - reception of signals is disabled for current thread; + */ + +std::pair<int, SignalInformation> tryWait(const SignalSet& signalSet); + +/** + * \brief Tries to wait for signals for given duration of time. + * + * Similar to sigtimedwait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html + * + * This function shall select the lowest pending signal from provided set, atomically clear it from current thread's set + * of pending signals and return that signal number. If no signal in provided set is pending at the time of the call, + * the thread shall be suspended until one or more becomes pending or until given duration of time expires. + * + * \param [in] signalSet is a reference to set of signals that will be waited for + * \param [in] duration is the duration after which the wait for signals will be terminated + * + * \return pair with return code (0 on success, error code otherwise) and SignalInformation object for accepted signal; + * error codes: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ENOTSUP - reception of signals is disabled for current thread; + * - ETIMEDOUT - no signal specified by \a signalSet was generated before the specified \a duration passed; + */ + +std::pair<int, SignalInformation> tryWaitFor(const SignalSet& signalSet, TickClock::duration duration); + +/** + * \brief Tries to wait for signals for given duration of time. + * + * Template variant of tryWaitFor(const SignalSet&, TickClock::duration). + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] signalSet is a reference to set of signals that will be waited for + * \param [in] duration is the duration after which the wait for signals will be terminated + * + * \return pair with return code (0 on success, error code otherwise) and SignalInformation object for accepted signal; + * error codes: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ENOTSUP - reception of signals is disabled for current thread; + * - ETIMEDOUT - no signal specified by \a signalSet was generated before the specified \a duration passed; + */ + +template<typename Rep, typename Period> +std::pair<int, SignalInformation> tryWaitFor(const SignalSet& signalSet, + const std::chrono::duration<Rep, Period> duration) +{ + return tryWaitFor(signalSet, std::chrono::duration_cast<TickClock::duration>(duration)); +} + +/** + * \brief Tries to wait for signals until given time point. + * + * Similar to sigtimedwait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html + * + * This function shall select the lowest pending signal from provided set, atomically clear it from current thread's set + * of pending signals and return that signal number. If no signal in provided set is pending at the time of the call, + * the thread shall be suspended until one or more becomes pending or until given time point is reached + * + * \param [in] signalSet is a reference to set of signals that will be waited for + * \param [in] timePoint is the time point at which the wait for signals will be terminated + * + * \return pair with return code (0 on success, error code otherwise) and SignalInformation object for accepted signal; + * error codes: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ENOTSUP - reception of signals is disabled for current thread; + * - ETIMEDOUT - no signal specified by \a signalSet was generated before specified \a timePoint; + */ + +std::pair<int, SignalInformation> tryWaitUntil(const SignalSet& signalSet, TickClock::time_point timePoint); + +/** + * \brief Tries to wait for signals until given time point. + * + * Template variant of tryWaitUntil(const SignalSet&, TickClock::time_point). + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] signalSet is a reference to set of signals that will be waited for + * \param [in] timePoint is the time point at which the wait for signals will be terminated + * + * \return pair with return code (0 on success, error code otherwise) and SignalInformation object for accepted signal; + * error codes: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ENOTSUP - reception of signals is disabled for current thread; + * - ETIMEDOUT - no signal specified by \a signalSet was generated before specified \a timePoint; + */ + +template<typename Duration> +std::pair<int, SignalInformation> tryWaitUntil(const SignalSet& signalSet, + const std::chrono::time_point<TickClock, Duration> timePoint) +{ + return tryWaitUntil(signalSet, std::chrono::time_point_cast<TickClock::duration>(timePoint)); +} + +/** + * \brief Waits for signals. + * + * Similar to sigwait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigwait.html + * + * This function shall select the lowest pending signal from provided set, atomically clear it from current thread's set + * of pending signals and return that signal number. If no signal in provided set is pending at the time of the call, + * the thread shall be suspended until one or more becomes pending. + * + * \param [in] signalSet is a reference to set of signals that will be waited for + * + * \return pair with return code (0 on success, error code otherwise) and SignalInformation object for accepted signal; + * error codes: + * - EINTR - the wait was interrupted by an unmasked, caught signal; + * - ENOTSUP - reception of signals is disabled for current thread; + */ + +std::pair<int, SignalInformation> wait(const SignalSet& signalSet); + +/// \} + +} // namespace Signals + +} // namespace ThisThread + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_THISTHREAD_SIGNALS_HPP_ diff --git a/include/distortos/ThisThread.hpp b/include/distortos/ThisThread.hpp new file mode 100644 index 0000000..5528412 --- /dev/null +++ b/include/distortos/ThisThread.hpp @@ -0,0 +1,158 @@ +/** + * \file + * \brief ThisThread namespace header + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_THISTHREAD_HPP_ +#define INCLUDE_DISTORTOS_THISTHREAD_HPP_ + +#include "distortos/TickClock.hpp" + +namespace distortos +{ + +class Thread; + +namespace ThisThread +{ + +/// \addtogroup threads +/// \{ + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +/** + * \brief Detaches calling (current) thread. + * + * Similar to std::thread::detach() - http://en.cppreference.com/w/cpp/thread/thread/detach + * Similar to POSIX pthread_detach() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html + * + * Detaches calling (current) thread from its Thread object, allowing execution to continue independently. All resources + * allocated for the thread will be deallocated when the thread terminates. + * + * \return 0 on success, error code otherwise: + * - EINVAL - current thread is already detached; + * - ENOTSUP - current thread cannot be detached; + */ + +int detach(); + +#endif // def CONFIG_THREAD_DETACH_ENABLE + +/** + * \return reference to Thread object of currently active thread + */ + +Thread& get(); + +/** + * \return effective priority of calling (current) thread + */ + +uint8_t getEffectivePriority(); + +/** + * \return priority of calling (current) thread + */ + +uint8_t getPriority(); + +/** + * Changes priority of calling (current) thread. + * + * \param [in] priority is the new priority for thread + * \param [in] alwaysBehind selects the method of ordering when lowering the priority + * - false - the thread is moved to the head of the group of threads with the new priority (default), + * - true - the thread is moved to the tail of the group of threads with the new priority. + */ + +void setPriority(uint8_t priority, bool alwaysBehind = {}); + +/** + * \brief Makes the calling (current) thread sleep for at least given duration. + * + * Current thread's state is changed to "sleeping". + * + * \note To fulfill the "at least" requirement, one additional tick is always added to the sleep duration. + * + * \param [in] duration is the duration after which the thread will be woken + * + * \return 0 on success, error code otherwise: + * - EINTR - the sleep was interrupted by an unmasked, caught signal; + */ + +int sleepFor(TickClock::duration duration); + +/** + * \brief Makes the calling (current) thread sleep for at least given duration. + * + * Current thread's state is changed to "sleeping". + * + * \note To fulfill the "at least" requirement, one additional tick is always added to the sleep duration. + * + * \tparam Rep is type of tick counter + * \tparam Period is std::ratio type representing the tick period of the clock, in seconds + * + * \param [in] duration is the duration after which the thread will be woken + * + * \return 0 on success, error code otherwise: + * - EINTR - the sleep was interrupted by an unmasked, caught signal; + */ + +template<typename Rep, typename Period> +int sleepFor(const std::chrono::duration<Rep, Period> duration) +{ + return sleepFor(std::chrono::duration_cast<TickClock::duration>(duration)); +} + +/** + * \brief Makes the calling (current) thread sleep until some time point is reached. + * + * Current thread's state is changed to "sleeping". + * + * \param [in] timePoint is the time point at which the thread will be woken + * + * \return 0 on success, error code otherwise: + * - EINTR - the sleep was interrupted by an unmasked, caught signal; + */ + +int sleepUntil(TickClock::time_point timePoint); + +/** + * \brief Makes the calling (current) thread sleep until some time point is reached. + * + * Current thread's state is changed to "sleeping". + * + * \tparam Duration is a std::chrono::duration type used to measure duration + * + * \param [in] timePoint is the time point at which the thread will be woken + * + * \return 0 on success, error code otherwise: + * - EINTR - the sleep was interrupted by an unmasked, caught signal; + */ + +template<typename Duration> +int sleepUntil(const std::chrono::time_point<TickClock, Duration> timePoint) +{ + return sleepUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint)); +} + +/** + * \brief Yields time slot of the scheduler to next thread. + */ + +void yield(); + +/// \} + +} // namespace ThisThread + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_THISTHREAD_HPP_ diff --git a/include/distortos/Thread.hpp b/include/distortos/Thread.hpp new file mode 100644 index 0000000..54139c1 --- /dev/null +++ b/include/distortos/Thread.hpp @@ -0,0 +1,194 @@ +/** + * \file + * \brief Thread class header + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_THREAD_HPP_ +#define INCLUDE_DISTORTOS_THREAD_HPP_ + +#include "distortos/distortosConfiguration.h" + +#include "distortos/SchedulingPolicy.hpp" +#include "distortos/SignalSet.hpp" +#include "distortos/ThreadState.hpp" + +#include <csignal> + +namespace distortos +{ + +/** + * \brief Thread class is a pure abstract interface for threads + * + * \ingroup threads + */ + +class Thread +{ +public: + + /** + * \brief Thread's destructor + */ + + virtual ~Thread() = 0; + +#ifdef CONFIG_THREAD_DETACH_ENABLE + + /** + * \brief Detaches the thread. + * + * Similar to std::thread::detach() - http://en.cppreference.com/w/cpp/thread/thread/detach + * Similar to POSIX pthread_detach() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html + * + * Detaches the executing thread from the Thread object, allowing execution to continue independently. All resources + * allocated for the thread will be deallocated when the thread terminates. + * + * \return 0 on success, error code otherwise: + * - EINVAL - this thread is already detached; + * - ENOTSUP - this thread cannot be detached; + */ + + virtual int detach() = 0; + +#endif // def CONFIG_THREAD_DETACH_ENABLE + + /** + * \brief Generates signal for thread. + * + * Similar to pthread_kill() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html + * + * Adds the signalNumber to set of pending signals. If this thread is currently waiting for this signal, it will be + * unblocked. + * + * \param [in] signalNumber is the signal that will be generated, [0; 31] + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception of signals is disabled for this thread; + * + * \ingroup signals + */ + + virtual int generateSignal(uint8_t signalNumber) = 0; + + /** + * \return effective priority of thread + */ + + virtual uint8_t getEffectivePriority() const = 0; + + /** + * \brief Gets set of currently pending signals. + * + * Similar to sigpending() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigpending.html + * + * This function shall return the set of signals that are blocked from delivery and are pending on the thread. + * + * \return set of currently pending signals + * + * \ingroup signals + */ + + virtual SignalSet getPendingSignalSet() const = 0; + + /** + * \return priority of thread + */ + + virtual uint8_t getPriority() const = 0; + + /** + * \return scheduling policy of the thread + */ + + virtual SchedulingPolicy getSchedulingPolicy() const = 0; + + /** + * \return current state of thread + */ + + virtual ThreadState getState() const = 0; + + /** + * \brief Waits for thread termination. + * + * Similar to std::thread::join() - http://en.cppreference.com/w/cpp/thread/thread/join + * Similar to POSIX pthread_join() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_join.html + * + * Blocks current thread until this thread finishes its execution. The results of multiple simultaneous calls to + * join() on the same target thread are undefined. + * + * \return 0 on success, error code otherwise: + * - EDEADLK - deadlock condition was detected, + * - EINVAL - this thread is not joinable, + * - ... + * + * \ingroup synchronization + */ + + virtual int join() = 0; + + /** + * \brief Queues signal for thread. + * + * Similar to sigqueue() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigqueue.html + * + * Adds the signalNumber and signal value (sigval union) to queue of SignalInformation objects. If this thread is + * currently waiting for this signal, it will be unblocked. + * + * \param [in] signalNumber is the signal that will be queued, [0; 31] + * \param [in] value is the signal value + * + * \return 0 on success, error code otherwise: + * - EAGAIN - no resources are available to queue the signal, maximal number of signals is already queued in + * associated queue of SignalInformation objects; + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception or queuing of signals are disabled for this thread; + * + * \ingroup signals + */ + + virtual int queueSignal(uint8_t signalNumber, sigval value) = 0; + + /** + * \brief Changes priority of thread. + * + * If the priority really changes, the position in the thread list is adjusted and context switch may be requested. + * + * \param [in] priority is the new priority of thread + * \param [in] alwaysBehind selects the method of ordering when lowering the priority + * - false - the thread is moved to the head of the group of threads with the new priority (default), + * - true - the thread is moved to the tail of the group of threads with the new priority. + */ + + virtual void setPriority(uint8_t priority, bool alwaysBehind = {}) = 0; + + /** + * param [in] schedulingPolicy is the new scheduling policy of the thread + */ + + virtual void setSchedulingPolicy(SchedulingPolicy schedulingPolicy) = 0; + + /** + * \brief Starts the thread. + * + * This operation can be performed on threads in "New" state only. + * + * \return 0 on success, error code otherwise: + * - EINVAL - thread is already started; + * - error codes returned by scheduler::Scheduler::add(); + */ + + virtual int start() = 0; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_THREAD_HPP_ diff --git a/include/distortos/ThreadCommon.hpp b/include/distortos/ThreadCommon.hpp new file mode 100644 index 0000000..8f34290 --- /dev/null +++ b/include/distortos/ThreadCommon.hpp @@ -0,0 +1,230 @@ +/** + * \file + * \brief ThreadCommon class header + * + * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_THREADCOMMON_HPP_ +#define INCLUDE_DISTORTOS_THREADCOMMON_HPP_ + +#include "distortos/internal/scheduler/ThreadControlBlock.hpp" + +#include "distortos/Semaphore.hpp" +#include "distortos/Thread.hpp" + +namespace distortos +{ + +/** + * \brief ThreadCommon class implements common functionality of threads + * + * \ingroup threads + */ + +class ThreadCommon : public Thread +{ +public: + + /// unique_ptr (with deleter) to storage for stack + using StackStorageUniquePointer = architecture::Stack::StorageUniquePointer; + + /** + * \brief ThreadCommon's constructor + * + * \param [in] stack is an rvalue reference to architecture::Stack object which will be adopted for this thread + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] threadGroupControlBlock is a pointer to internal::ThreadGroupControlBlock to which this object will + * be added, nullptr to inherit thread group from currently running thread + * \param [in] signalsReceiver is a pointer to SignalsReceiver object for this thread, nullptr to disable reception + * of signals for this thread + */ + + ThreadCommon(architecture::Stack&& stack, uint8_t priority, SchedulingPolicy schedulingPolicy, + internal::ThreadGroupControlBlock* threadGroupControlBlock, SignalsReceiver* signalsReceiver); + + /** + * \brief ThreadCommon's destructor + */ + + ~ThreadCommon() override; + + /** + * \brief Generates signal for thread. + * + * Similar to pthread_kill() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html + * + * Adds the signalNumber to set of pending signals. If this thread is currently waiting for this signal, it will be + * unblocked. + * + * \param [in] signalNumber is the signal that will be generated, [0; 31] + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception of signals is disabled for this thread; + * + * \ingroup signals + */ + + int generateSignal(uint8_t signalNumber) override; + + /** + * \return effective priority of thread + */ + + uint8_t getEffectivePriority() const override; + + /** + * \brief Gets set of currently pending signals. + * + * Similar to sigpending() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigpending.html + * + * This function shall return the set of signals that are blocked from delivery and are pending on the thread. + * + * \return set of currently pending signals + * + * \ingroup signals + */ + + SignalSet getPendingSignalSet() const override; + + /** + * \return priority of thread + */ + + uint8_t getPriority() const override; + + /** + * \return scheduling policy of the thread + */ + + SchedulingPolicy getSchedulingPolicy() const override; + + /** + * \return current state of thread + */ + + ThreadState getState() const override; + + /** + * \brief Waits for thread termination. + * + * Similar to std::thread::join() - http://en.cppreference.com/w/cpp/thread/thread/join + * Similar to POSIX pthread_join() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_join.html + * + * Blocks current thread until this thread finishes its execution. The results of multiple simultaneous calls to + * join() on the same target thread are undefined. + * + * \return 0 on success, error code otherwise: + * - EDEADLK - deadlock condition was detected, + * - EINVAL - this thread is not joinable, + * - ... + * + * \ingroup synchronization + */ + + int join() override; + + /** + * \brief Queues signal for thread. + * + * Similar to sigqueue() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigqueue.html + * + * Adds the signalNumber and signal value (sigval union) to queue of SignalInformation objects. If this thread is + * currently waiting for this signal, it will be unblocked. + * + * \param [in] signalNumber is the signal that will be queued, [0; 31] + * \param [in] value is the signal value + * + * \return 0 on success, error code otherwise: + * - EAGAIN - no resources are available to queue the signal, maximal number of signals is already queued in + * associated queue of SignalInformation objects; + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - reception or queuing of signals are disabled for this thread; + * + * \ingroup signals + */ + + int queueSignal(uint8_t signalNumber, sigval value) override; + + /** + * \brief Changes priority of thread. + * + * If the priority really changes, the position in the thread list is adjusted and context switch may be requested. + * + * \param [in] priority is the new priority of thread + * \param [in] alwaysBehind selects the method of ordering when lowering the priority + * - false - the thread is moved to the head of the group of threads with the new priority (default), + * - true - the thread is moved to the tail of the group of threads with the new priority. + */ + + void setPriority(uint8_t priority, bool alwaysBehind = {}) override; + + /** + * param [in] schedulingPolicy is the new scheduling policy of the thread + */ + + void setSchedulingPolicy(SchedulingPolicy schedulingPolicy) override; + + /** + * \brief Starts the thread. + * + * This operation can be performed on threads in "New" state only. + * + * \return 0 on success, error code otherwise: + * - EINVAL - thread is already started; + * - error codes returned by internal::Scheduler::add(); + */ + + int start() override; + + ThreadCommon(const ThreadCommon&) = delete; + ThreadCommon(ThreadCommon&&) = default; + const ThreadCommon& operator=(const ThreadCommon&) = delete; + ThreadCommon& operator=(ThreadCommon&&) = delete; + +protected: + + /** + * \return reference to internal ThreadControlBlock object + */ + + internal::ThreadControlBlock& getThreadControlBlock() + { + return threadControlBlock_; + } + + /** + * \return const reference to internal ThreadControlBlock object + */ + + const internal::ThreadControlBlock& getThreadControlBlock() const + { + return threadControlBlock_; + } + + /** + * \brief Termination hook function of thread + * + * \param [in] thread is a reference to Thread object, this must be ThreadCommon! + */ + + static void terminationHook(Thread& thread); + +private: + + /// internal ThreadControlBlock object + internal::ThreadControlBlock threadControlBlock_; + + /// semaphore used by join() + Semaphore joinSemaphore_; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_THREADCOMMON_HPP_ diff --git a/include/distortos/ThreadState.hpp b/include/distortos/ThreadState.hpp new file mode 100644 index 0000000..15a92e3 --- /dev/null +++ b/include/distortos/ThreadState.hpp @@ -0,0 +1,54 @@ +/** + * \file + * \brief ThreadState enum class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_THREADSTATE_HPP_ +#define INCLUDE_DISTORTOS_THREADSTATE_HPP_ + +#include <cstdint> + +namespace distortos +{ + +/** + * \brief state of the thread + * + * \ingroup threads + */ + +enum class ThreadState : uint8_t +{ + /// state in which thread is created, before being added to Scheduler + created, + /// thread is runnable + runnable, + /// thread is terminated + terminated, + /// thread is sleeping + sleeping, + /// thread is blocked on Semaphore + blockedOnSemaphore, + /// thread is suspended + suspended, + /// thread is blocked on Mutex + blockedOnMutex, + /// thread is blocked on ConditionVariable + blockedOnConditionVariable, + /// thread is waiting for signal + waitingForSignal, + /// thread is blocked on OnceFlag + blockedOnOnceFlag, + /// internal thread object was detached + detached, +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_THREADSTATE_HPP_ diff --git a/include/distortos/TickClock.hpp b/include/distortos/TickClock.hpp new file mode 100644 index 0000000..7f1ace0 --- /dev/null +++ b/include/distortos/TickClock.hpp @@ -0,0 +1,56 @@ +/** + * \file + * \brief TickClock class header + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_TICKCLOCK_HPP_ +#define INCLUDE_DISTORTOS_TICKCLOCK_HPP_ + +#include "distortos/distortosConfiguration.h" + +#include <chrono> + +namespace distortos +{ + +/** + * \brief TickClock is a std::chrono clock, equivalent of std::chrono::steady_clock + * + * \ingroup clocks + */ + +class TickClock +{ +public: + + /// type of tick counter + using rep = uint64_t; + + /// std::ratio type representing the tick period of the clock, in seconds + using period = std::ratio<1, CONFIG_TICK_FREQUENCY>; + + /// basic duration type of clock + using duration = std::chrono::duration<rep, period>; + + /// basic time_point type of clock + using time_point = std::chrono::time_point<TickClock>; + + /** + * \return time_point representing the current value of the clock + */ + + static time_point now(); + + /// this is a steady clock - it cannot be adjusted + static constexpr bool is_steady {true}; +}; + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_TICKCLOCK_HPP_ diff --git a/include/distortos/UndetachableThread.hpp b/include/distortos/UndetachableThread.hpp new file mode 100644 index 0000000..772b9aa --- /dev/null +++ b/include/distortos/UndetachableThread.hpp @@ -0,0 +1,57 @@ +/** + * \file + * \brief UndetachableThread class header + * + * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_UNDETACHABLETHREAD_HPP_ +#define INCLUDE_DISTORTOS_UNDETACHABLETHREAD_HPP_ + +#include "distortos/ThreadCommon.hpp" + +namespace distortos +{ + +/** + * \brief UndetachableThread class is a ThreadCommon that cannot be detached + * + * \ingroup threads + */ + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +class UndetachableThread : public ThreadCommon +{ +public: + + using ThreadCommon::ThreadCommon; + + /** + * \brief Detaches the thread. + * + * Similar to std::thread::detach() - http://en.cppreference.com/w/cpp/thread/thread/detach + * Similar to POSIX pthread_detach() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html + * + * Detaches the executing thread from the Thread object, allowing execution to continue independently. All resources + * allocated for the thread will be deallocated when the thread terminates. + * + * \return ENOTSUP - this thread cannot be detached; + */ + + int detach() override; +}; + +#else // !def CONFIG_THREAD_DETACH_ENABLE + +using UndetachableThread = ThreadCommon; + +#endif // !def CONFIG_THREAD_DETACH_ENABLE + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_UNDETACHABLETHREAD_HPP_ diff --git a/include/distortos/architecture/InterruptMaskingLock.hpp b/include/distortos/architecture/InterruptMaskingLock.hpp new file mode 100644 index 0000000..6f8253b --- /dev/null +++ b/include/distortos/architecture/InterruptMaskingLock.hpp @@ -0,0 +1,34 @@ +/** + * \file + * \brief InterruptMaskingLock class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTMASKINGLOCK_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTMASKINGLOCK_HPP_ + +#include "distortos/architecture/InterruptMaskingUnmaskingLock.hpp" +#include "distortos/architecture/enableInterruptMasking.hpp" + +namespace distortos +{ + +namespace architecture +{ + +/// InterruptMaskingLock class is a RAII wrapper for enableInterruptMasking() / restoreInterruptMasking() +class InterruptMaskingLock : private InterruptMaskingUnmaskingLock<enableInterruptMasking> +{ + +}; + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTMASKINGLOCK_HPP_ diff --git a/include/distortos/architecture/InterruptMaskingUnmaskingLock.hpp b/include/distortos/architecture/InterruptMaskingUnmaskingLock.hpp new file mode 100644 index 0000000..65bd0f0 --- /dev/null +++ b/include/distortos/architecture/InterruptMaskingUnmaskingLock.hpp @@ -0,0 +1,73 @@ +/** + * \file + * \brief InterruptMaskingUnmaskingLock class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTMASKINGUNMASKINGLOCK_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTMASKINGUNMASKINGLOCK_HPP_ + +#include "distortos/architecture/restoreInterruptMasking.hpp" + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief InterruptMaskingUnmaskingLock class is a RAII wrapper for interrupt mask manipulation. + * + * \tparam Function is a reference to function which modifies interrupt mask and returns InterruptMask; + * enableInterruptMasking() or disableInterruptMasking() should be used + */ + +template<InterruptMask (& Function)()> +class InterruptMaskingUnmaskingLock +{ +public: + + /** + * \brief InterruptMaskingUnmaskingLock's constructor + * + * Enables/disables interrupt masking, saving current interrupt mask for use in destructor. + */ + + InterruptMaskingUnmaskingLock() : + interruptMask_{Function()} + { + + } + + /** + * \brief InterruptMaskingUnmaskingLock's destructor + * + * Restores previous interrupt masking state by restoring interrupt mask saved in constructor. + */ + + ~InterruptMaskingUnmaskingLock() + { + restoreInterruptMasking(interruptMask_); + } + + InterruptMaskingUnmaskingLock(const InterruptMaskingUnmaskingLock&) = delete; + InterruptMaskingUnmaskingLock(InterruptMaskingUnmaskingLock&&) = delete; + InterruptMaskingUnmaskingLock& operator=(const InterruptMaskingUnmaskingLock&) = delete; + InterruptMaskingUnmaskingLock& operator=(InterruptMaskingUnmaskingLock&&) = delete; + +private: + + /// interrupt mask + const InterruptMask interruptMask_; +}; + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTMASKINGUNMASKINGLOCK_HPP_ diff --git a/include/distortos/architecture/InterruptUnmaskingLock.hpp b/include/distortos/architecture/InterruptUnmaskingLock.hpp new file mode 100644 index 0000000..263bbea --- /dev/null +++ b/include/distortos/architecture/InterruptUnmaskingLock.hpp @@ -0,0 +1,34 @@ +/** + * \file + * \brief InterruptUnmaskingLock class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTUNMASKINGLOCK_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTUNMASKINGLOCK_HPP_ + +#include "distortos/architecture/InterruptMaskingUnmaskingLock.hpp" +#include "distortos/architecture/disableInterruptMasking.hpp" + +namespace distortos +{ + +namespace architecture +{ + +/// InterruptUnmaskingLock class is a RAII wrapper for disableInterruptMasking() / restoreInterruptMasking() +class InterruptUnmaskingLock : private InterruptMaskingUnmaskingLock<disableInterruptMasking> +{ + +}; + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_INTERRUPTUNMASKINGLOCK_HPP_ diff --git a/include/distortos/architecture/Stack.hpp b/include/distortos/architecture/Stack.hpp new file mode 100644 index 0000000..bd23452 --- /dev/null +++ b/include/distortos/architecture/Stack.hpp @@ -0,0 +1,122 @@ +/** + * \file + * \brief Stack class header + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_STACK_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_STACK_HPP_ + +#include <memory> + +namespace distortos +{ + +class Thread; + +namespace architecture +{ + +/// Stack class is an abstraction of architecture's stack +class Stack +{ +public: + + /// unique_ptr (with deleter) to storage + using StorageUniquePointer = std::unique_ptr<void, void(&)(void*)>; + + /** + * \brief Stack's constructor + * + * This function initializes valid architecture-specific stack in provided storage. This requires following steps: + * - adjustment of storage's address to suit architecture's alignment requirements, + * - adjustment of storage's size to suit architecture's divisibility requirements, + * - creating hardware and software stack frame in suitable place in the stack, + * - calculation of stack pointer register value. + * + * After this constructor completes, stack is ready for context switching. + * + * \param [in] storageUniquePointer is a rvalue reference to StorageUniquePointer with storage for stack (\a size + * bytes long) and appropriate deleter + * \param [in] size is the size of stack's storage, bytes + * \param [in] thread is a reference to Thread object passed to function + * \param [in] run is a reference to Thread's "run" function + * \param [in] preTerminationHook is a pointer to Thread's pre-termination hook, nullptr to skip + * \param [in] terminationHook is a reference to Thread's termination hook + */ + + Stack(StorageUniquePointer&& storageUniquePointer, size_t size, Thread& thread, void (& run)(Thread&), + void (* preTerminationHook)(Thread&), void (& terminationHook)(Thread&)); + + /** + * \brief Stack's constructor + * + * This function adopts existing valid architecture-specific stack in provided storage. No adjustments are done, + * no stack frame is created and stack pointer register's value is not calculated. + * + * This is meant to adopt main()'s stack. + * + * \param [in] storage is a pointer to stack's storage + * \param [in] size is the size of stack's storage, bytes + */ + + Stack(void* storage, size_t size); + + /** + * \brief Stack's destructor + */ + + ~Stack(); + + /** + * \brief Gets current value of stack pointer. + * + * \return current value of stack pointer + */ + + void* getStackPointer() const + { + return stackPointer_; + } + + /** + * \brief Sets value of stack pointer. + * + * \param [in] stackPointer is the new value of stack pointer + */ + + void setStackPointer(void* const stackPointer) + { + stackPointer_ = stackPointer; + } + + Stack(const Stack&) = delete; + Stack(Stack&&) = default; + const Stack& operator=(const Stack&) = delete; + Stack& operator=(Stack&&) = delete; + +private: + + /// storage for stack + StorageUniquePointer storageUniquePointer_; + + /// adjusted address of stack's storage + void* const adjustedStorage_; + + /// adjusted size of stack's storage + const size_t adjustedSize_; + + /// current value of stack pointer register + void* stackPointer_; +}; + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_STACK_HPP_ diff --git a/include/distortos/architecture/disableInterruptMasking.hpp b/include/distortos/architecture/disableInterruptMasking.hpp new file mode 100644 index 0000000..d82eda1 --- /dev/null +++ b/include/distortos/architecture/disableInterruptMasking.hpp @@ -0,0 +1,37 @@ +/** + * \file + * \brief disableInterruptMasking() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_DISABLEINTERRUPTMASKING_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_DISABLEINTERRUPTMASKING_HPP_ + +#include "distortos/architecture/parameters.hpp" + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief Disables interrupt masking. + * + * Enables normal-priority interrupts. + * + * \return previous value of interrupts' mask, must be used for matched restoreInterruptMasking() call + */ + +InterruptMask disableInterruptMasking(); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_DISABLEINTERRUPTMASKING_HPP_ diff --git a/include/distortos/architecture/enableInterruptMasking.hpp b/include/distortos/architecture/enableInterruptMasking.hpp new file mode 100644 index 0000000..8fb6566 --- /dev/null +++ b/include/distortos/architecture/enableInterruptMasking.hpp @@ -0,0 +1,40 @@ +/** + * \file + * \brief enableInterruptMasking() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_ENABLEINTERRUPTMASKING_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_ENABLEINTERRUPTMASKING_HPP_ + +#include "distortos/architecture/parameters.hpp" + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief Enables interrupt masking. + * + * Disables normal-priority interrupts. + * + * \note High-priority interrupts are not controlled by distortos, so they may be left enabled. Support for that feature + * is architecture-dependent. + * + * \return previous value of interrupts' mask, must be used for matched restoreInterruptMasking() call + */ + +InterruptMask enableInterruptMasking(); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_ENABLEINTERRUPTMASKING_HPP_ diff --git a/include/distortos/architecture/getMainStack.hpp b/include/distortos/architecture/getMainStack.hpp new file mode 100644 index 0000000..6f75c2b --- /dev/null +++ b/include/distortos/architecture/getMainStack.hpp @@ -0,0 +1,37 @@ +/** + * \file + * \brief getMainStack() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_GETMAINSTACK_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_GETMAINSTACK_HPP_ + +#include <utility> + +#include <cstddef> + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief Gets the stack used to run main(). + * + * \return beginning of stack and its size in bytes + */ + +std::pair<void*, size_t> getMainStack(); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_GETMAINSTACK_HPP_ diff --git a/include/distortos/architecture/initializeStack.hpp b/include/distortos/architecture/initializeStack.hpp new file mode 100644 index 0000000..6cc73ae --- /dev/null +++ b/include/distortos/architecture/initializeStack.hpp @@ -0,0 +1,50 @@ +/** + * \file + * \brief initializeStack() declaration + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_INITIALIZESTACK_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_INITIALIZESTACK_HPP_ + +#include <cstddef> + +namespace distortos +{ + +class Thread; + +namespace architecture +{ + +/** + * \brief Architecture-specific stack initialization. + * + * This function fills provided buffer with hardware and software stack frame and calculates value of stack pointer + * register. After this function completes, stack is ready for context switching. + * + * \attention buffer and size must be properly adjusted for architecture requirements + * + * \param [in] buffer is a pointer to stack's buffer + * \param [in] size is the size of stack's buffer, bytes + * \param [in] thread is a reference to Thread object passed to function + * \param [in] run is a reference to Thread's "run" function + * \param [in] preTerminationHook is a pointer to Thread's pre-termination hook, nullptr to skip + * \param [in] terminationHook is a reference to Thread's termination hook + * + * \return value that can be used as thread's stack pointer, ready for context switching + */ + +void* initializeStack(void* buffer, size_t size, Thread& thread, void (& run)(Thread&), + void (* preTerminationHook)(Thread&), void (& terminationHook)(Thread&)); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_INITIALIZESTACK_HPP_ diff --git a/include/distortos/architecture/lowLevelInitialization.hpp b/include/distortos/architecture/lowLevelInitialization.hpp new file mode 100644 index 0000000..4c9d3d2 --- /dev/null +++ b/include/distortos/architecture/lowLevelInitialization.hpp @@ -0,0 +1,34 @@ +/** + * \file + * \brief lowLevelInitialization() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_LOWLEVELINITIALIZATION_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_LOWLEVELINITIALIZATION_HPP_ + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief Low level architecture initialization. + * + * This function is called before constructors for global and static objects from __libc_init_array() via address in + * distortosPreinitArray[]. + */ + +void lowLevelInitialization(); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_LOWLEVELINITIALIZATION_HPP_ diff --git a/include/distortos/architecture/requestContextSwitch.hpp b/include/distortos/architecture/requestContextSwitch.hpp new file mode 100644 index 0000000..bc92b6b --- /dev/null +++ b/include/distortos/architecture/requestContextSwitch.hpp @@ -0,0 +1,35 @@ +/** + * \file + * \brief requestContextSwitch() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_REQUESTCONTEXTSWITCH_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_REQUESTCONTEXTSWITCH_HPP_ + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief Architecture-specific request for context switch. + * + * Causes the architecture to do context save, call scheduler::getScheduler().switchContext() and do context restore. + * The call to scheduler::getScheduler().switchContext() must be done from the context in which such call is valid + * (usually system interrupt). + */ + +void requestContextSwitch(); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_REQUESTCONTEXTSWITCH_HPP_ diff --git a/include/distortos/architecture/requestFunctionExecution.hpp b/include/distortos/architecture/requestFunctionExecution.hpp new file mode 100644 index 0000000..69c9eda --- /dev/null +++ b/include/distortos/architecture/requestFunctionExecution.hpp @@ -0,0 +1,53 @@ +/** + * \file + * \brief requestFunctionExecution() declaration + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_REQUESTFUNCTIONEXECUTION_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_REQUESTFUNCTIONEXECUTION_HPP_ + +namespace distortos +{ + +namespace internal +{ + +class ThreadControlBlock; + +} // namespace internal + +namespace architecture +{ + +/** + * \brief Requests execution of provided function in the specified thread. + * + * Main use case for this function is to request execution of signals delivery function. In such case it is called when + * an unblocked signal, which is not ignored, is generated or queued for specified thread. + * + * It must arrange for given function to be executed in specified thread as soon as possible. This generally requires + * dealing with following scenarios: + * - current thread is sending the request to itself - in that case just execute function right away; + * - current thread is sending the request to non-current thread; + * - interrupt is sending the request to current thread; + * - interrupt is sending the request to non-current thread; + * + * \param [in] threadControlBlock is a reference to internal::ThreadControlBlock of thread in which \a function should + * be executed + * \param [in] function is a reference to function that should be executed in thread associated with + * \a threadControlBlock + */ + +void requestFunctionExecution(internal::ThreadControlBlock& threadControlBlock, void (& function)()); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_REQUESTFUNCTIONEXECUTION_HPP_ diff --git a/include/distortos/architecture/restoreInterruptMasking.hpp b/include/distortos/architecture/restoreInterruptMasking.hpp new file mode 100644 index 0000000..268159b --- /dev/null +++ b/include/distortos/architecture/restoreInterruptMasking.hpp @@ -0,0 +1,39 @@ +/** + * \file + * \brief restoreInterruptMasking() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_RESTOREINTERRUPTMASKING_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_RESTOREINTERRUPTMASKING_HPP_ + +#include "distortos/architecture/parameters.hpp" + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief Restores interrupt masking. + * + * Restores previous interrupt masking state (before matching enableInterruptMasking() or disableInterruptMasking() was + * called), enabling some (maybe all, maybe none) interrupts. + * + * \param [in] interruptMask is the value of interrupts' mask, must come from previous call to enableInterruptMasking() + * or disableInterruptMasking() + */ + +void restoreInterruptMasking(InterruptMask interruptMask); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_RESTOREINTERRUPTMASKING_HPP_ diff --git a/include/distortos/architecture/startScheduling.hpp b/include/distortos/architecture/startScheduling.hpp new file mode 100644 index 0000000..20db514 --- /dev/null +++ b/include/distortos/architecture/startScheduling.hpp @@ -0,0 +1,33 @@ +/** + * \file + * \brief startScheduling() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_ARCHITECTURE_STARTSCHEDULING_HPP_ +#define INCLUDE_DISTORTOS_ARCHITECTURE_STARTSCHEDULING_HPP_ + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief Architecture-specific start of scheduling. + * + * Initializes all required hardware/software to perform context switching and starts the scheduling. + */ + +void startScheduling(); + +} // namespace architecture + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_ARCHITECTURE_STARTSCHEDULING_HPP_ diff --git a/include/distortos/board/lowLevelInitialization.hpp b/include/distortos/board/lowLevelInitialization.hpp new file mode 100644 index 0000000..6007422 --- /dev/null +++ b/include/distortos/board/lowLevelInitialization.hpp @@ -0,0 +1,36 @@ +/** + * \file + * \brief board::lowLevelInitialization() declaration + * + * \author Copyright (C) 2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_BOARD_LOWLEVELINITIALIZATION_HPP_ +#define INCLUDE_DISTORTOS_BOARD_LOWLEVELINITIALIZATION_HPP_ + +namespace distortos +{ + +namespace board +{ + +/** + * \brief Low level board initialization. + * + * This function is called before constructors for global and static objects from __libc_init_array() via address in + * distortosPreinitArray[]. + * + * \note Use of this function is optional - it may be left undefined, in which case it will not be called. + */ + +void lowLevelInitialization() __attribute__ ((weak)); + +} // namespace board + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_BOARD_LOWLEVELINITIALIZATION_HPP_ diff --git a/include/distortos/callOnce.hpp b/include/distortos/callOnce.hpp new file mode 100644 index 0000000..ed1cd1d --- /dev/null +++ b/include/distortos/callOnce.hpp @@ -0,0 +1,50 @@ +/** + * \file + * \brief callOnce() header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_CALLONCE_HPP_ +#define INCLUDE_DISTORTOS_CALLONCE_HPP_ + +#include "distortos/OnceFlag.hpp" + +#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1 + +namespace distortos +{ + +/** + * \brief Executes the callable object exactly once, even if called from multiple threads. + * + * Similar to std::call_once() - http://en.cppreference.com/w/cpp/thread/call_once + * Similar to POSIX pthread_once() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_once.html# + * + * \note This function requires GCC 4.9. + * + * \tparam Function is the function object that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] onceFlag is a reference to shared OnceFlag object + * \param [in] function is the function object that will be executed + * \param [in] args are arguments for \a function + * + * \ingroup synchronization + */ + +template<typename Function, typename... Args> +void callOnce(OnceFlag& onceFlag, Function&& function, Args&&... args) +{ + onceFlag.callOnceControlBlock_(std::forward<Function>(function), std::forward<Args>(args)...); +} + +} // namespace distortos + +#endif // DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1 + +#endif // INCLUDE_DISTORTOS_CALLONCE_HPP_ diff --git a/include/distortos/chip/lowLevelInitialization.hpp b/include/distortos/chip/lowLevelInitialization.hpp new file mode 100644 index 0000000..f9e28ac --- /dev/null +++ b/include/distortos/chip/lowLevelInitialization.hpp @@ -0,0 +1,34 @@ +/** + * \file + * \brief chip::lowLevelInitialization() declaration + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_CHIP_LOWLEVELINITIALIZATION_HPP_ +#define INCLUDE_DISTORTOS_CHIP_LOWLEVELINITIALIZATION_HPP_ + +namespace distortos +{ + +namespace chip +{ + +/** + * \brief Low level chip initialization. + * + * This function is called before constructors for global and static objects from __libc_init_array() via address in + * distortosPreinitArray[]. + */ + +void lowLevelInitialization(); + +} // namespace chip + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_CHIP_LOWLEVELINITIALIZATION_HPP_ diff --git a/include/distortos/devices/io/InputPin.hpp b/include/distortos/devices/io/InputPin.hpp new file mode 100644 index 0000000..9838998 --- /dev/null +++ b/include/distortos/devices/io/InputPin.hpp @@ -0,0 +1,48 @@ +/** + * \file + * \brief InputPin class header + * + * \author Copyright (C) 2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DEVICES_IO_INPUTPIN_HPP_ +#define INCLUDE_DISTORTOS_DEVICES_IO_INPUTPIN_HPP_ + +namespace distortos +{ + +namespace devices +{ + +/** + * InputPin class is an interface for single input pin + * + * \ingroup devices + */ + +class InputPin +{ +public: + + /** + * \brief InputPin's destructor + */ + + virtual ~InputPin() = 0; + + /** + * \return current state of pin + */ + + virtual bool get() const = 0; +}; + +} // namespace devices + +} // namespace devices + +#endif // INCLUDE_DISTORTOS_DEVICES_IO_INPUTPIN_HPP_ diff --git a/include/distortos/devices/io/OutputPin.hpp b/include/distortos/devices/io/OutputPin.hpp new file mode 100644 index 0000000..6651d9d --- /dev/null +++ b/include/distortos/devices/io/OutputPin.hpp @@ -0,0 +1,46 @@ +/** + * \file + * \brief OutputPin class header + * + * \author Copyright (C) 2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DEVICES_IO_OUTPUTPIN_HPP_ +#define INCLUDE_DISTORTOS_DEVICES_IO_OUTPUTPIN_HPP_ + +#include "distortos/devices/io/InputPin.hpp" + +namespace distortos +{ + +namespace devices +{ + +/** + * OutputPin class is an interface for single output pin + * + * \ingroup devices + */ + +class OutputPin : public InputPin +{ +public: + + /** + * \brief Sets state of pin. + * + * \param [in] state is the new state of pin + */ + + virtual void set(bool state) = 0; +}; + +} // namespace devices + +} // namespace devices + +#endif // INCLUDE_DISTORTOS_DEVICES_IO_OUTPUTPIN_HPP_ diff --git a/include/distortos/distortosVersion.h b/include/distortos/distortosVersion.h new file mode 100644 index 0000000..554758d --- /dev/null +++ b/include/distortos/distortosVersion.h @@ -0,0 +1,37 @@ +/** + * \file + * \brief distortos version + * + * \author Copyright (C) 2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_DISTORTOSVERSION_H_ +#define INCLUDE_DISTORTOS_DISTORTOSVERSION_H_ + +/// major version of distortos +#define DISTORTOS_VERSION_MAJOR 0 + +/// minor version of distortos +#define DISTORTOS_VERSION_MINOR 2 + +/// patch version of distortos +#define DISTORTOS_VERSION_PATCH 0 + +/// distortos version as a 32-bit variable +#define DISTORTOS_VERSION ((DISTORTOS_VERSION_MAJOR << 16) | (DISTORTOS_VERSION_MINOR << 8) | DISTORTOS_VERSION_PATCH) + +/// internals of STRINGIZE() macro +#define STRINGIZE_INTERNAL(x) #x + +/// macro used for stringification +#define STRINGIZE(x) STRINGIZE_INTERNAL(x) + +/// distortos version as string +#define DISTORTOS_VERSION_STRING \ + STRINGIZE(DISTORTOS_VERSION_MAJOR) "." STRINGIZE(DISTORTOS_VERSION_MINOR) "." STRINGIZE(DISTORTOS_VERSION_PATCH) + +#endif /* INCLUDE_DISTORTOS_DISTORTOSVERSION_H_ */ diff --git a/include/distortos/internal/memory/DeferredThreadDeleter.hpp b/include/distortos/internal/memory/DeferredThreadDeleter.hpp new file mode 100644 index 0000000..bb38ebc --- /dev/null +++ b/include/distortos/internal/memory/DeferredThreadDeleter.hpp @@ -0,0 +1,121 @@ +/** + * \file + * \brief DeferredThreadDeleter class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_MEMORY_DEFERREDTHREADDELETER_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_MEMORY_DEFERREDTHREADDELETER_HPP_ + +#include "distortos/distortosConfiguration.h" + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +#include "distortos/Mutex.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// DeferredThreadDeleter class can be used to defer deletion of dynamic detached threads +class DeferredThreadDeleter +{ +public: + + /** + * \brief DeferredThreadDeleter's constructor + */ + + constexpr DeferredThreadDeleter() : + list_{}, + mutex_{Mutex::Type::normal, Mutex::Protocol::priorityInheritance}, + notEmpty_{} + { + + } + + /** + * \brief DeferredThreadDeleter's function call operator + * + * Adds thread to internal list of threads scheduled for deferred deletion and marks the list as "not empty". + * + * \note The object must be locked (with a successful call to DeferredThreadDeleter::lock()) before this function is + * used! + * + * \param [in] threadControlBlock is a reference to ThreadControlBlock object associated with dynamic and detached + * thread that has terminated its execution + * + * \return 0 on success, error code otherwise: + * - error codes returned by Mutex::unlock(); + */ + + int operator()(ThreadControlBlock& threadControlBlock); + + /** + * \brief Locks the object, preparing it for adding thread to internal list. + * + * Locks the mutex that synchronizes access to internal list. Locking (performed in this function) and unlocking + * (performed at the end of function call operator) are separated, because the locking must be done while the thread + * is still runnable, while the transfer to internal list is performed when the thread is not in this state. + * + * \note This function must be called before function call operator is used! + * + * \return 0 on success, error code otherwise: + * - error codes returned by Mutex::lock(); + */ + + int lock(); + + /** + * \brief Tries to perform deferred deletion of threads. + * + * Does nothing is the list is not marked as "not empty". Otherwise this function first tries to lock following two + * mutexes: + * - mutex that protects dynamic memory allocator; + * - mutex that synchronizes access to list of threads scheduled for deferred deletion; + * If any Mutex::tryLock() call fails, this function just returns (unlocking any mutexes is necessary). Otherwise + * the threads are removed from the list and deleted, while the list's "not empty" marker is cleared. + * + * \return 0 on success, error code otherwise: + * - error codes returned by Mutex::tryLock(); + * - error codes returned by Mutex::unlock(); + */ + + int tryCleanup(); + +private: + + /** + * \brief Internals of tryCleanup(). + * + * \return 0 on success, error code otherwise: + * - error codes returned by Mutex::tryLock(); + * - error codes returned by Mutex::unlock(); + */ + + int tryCleanupInternal(); + + /// list of threads scheduled for deferred deletion + ThreadList::UnsortedIntrusiveList list_; + + /// mutex that synchronizes access to the \a list_ + Mutex mutex_; + + /// true if \a list_ is not empty, false otherwise + bool notEmpty_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // def CONFIG_THREAD_DETACH_ENABLE + +#endif // INCLUDE_DISTORTOS_INTERNAL_MEMORY_DEFERREDTHREADDELETER_HPP_ diff --git a/include/distortos/internal/memory/dummyDeleter.hpp b/include/distortos/internal/memory/dummyDeleter.hpp new file mode 100644 index 0000000..3cee5cf --- /dev/null +++ b/include/distortos/internal/memory/dummyDeleter.hpp @@ -0,0 +1,48 @@ +/** + * \file + * \brief dummyDeleter() declaration + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_MEMORY_DUMMYDELETER_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_MEMORY_DUMMYDELETER_HPP_ + +#include <type_traits> + +namespace distortos +{ + +namespace internal +{ + +/*---------------------------------------------------------------------------------------------------------------------+ +| global functions' declarations ++---------------------------------------------------------------------------------------------------------------------*/ + +/** + * \brief A "no-op" dummy deleter that can be used with std::unique_ptr and automatic storage that is trivially + * destructible. + * + * \tparam T is the real type of storage, must be trivially destructible + * \tparam U is the type of \a storage pointer + * + * \param [in] storage is a pointer to storage + */ + +template<typename T, typename U> +void dummyDeleter(U*) +{ + static_assert(std::is_trivially_destructible<T>::value == true, + "internal::dummyDeleter() cannot be used with types that are not trivially destructible!"); +} + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_MEMORY_DUMMYDELETER_HPP_ diff --git a/include/distortos/internal/memory/getDeferredThreadDeleter.hpp b/include/distortos/internal/memory/getDeferredThreadDeleter.hpp new file mode 100644 index 0000000..30fea99 --- /dev/null +++ b/include/distortos/internal/memory/getDeferredThreadDeleter.hpp @@ -0,0 +1,39 @@ +/** + * \file + * \brief getDeferredThreadDeleter() declaration + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_MEMORY_GETDEFERREDTHREADDELETER_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_MEMORY_GETDEFERREDTHREADDELETER_HPP_ + +#include "distortos/distortosConfiguration.h" + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +namespace distortos +{ + +namespace internal +{ + +class DeferredThreadDeleter; + +/** + * \return reference to main instance of DeferredThreadDeleter + */ + +DeferredThreadDeleter& getDeferredThreadDeleter(); + +} // namespace internal + +} // namespace distortos + +#endif // def CONFIG_THREAD_DETACH_ENABLE + +#endif // INCLUDE_DISTORTOS_INTERNAL_MEMORY_GETDEFERREDTHREADDELETER_HPP_ diff --git a/include/distortos/internal/memory/getMallocMutex.hpp b/include/distortos/internal/memory/getMallocMutex.hpp new file mode 100644 index 0000000..0ba5621 --- /dev/null +++ b/include/distortos/internal/memory/getMallocMutex.hpp @@ -0,0 +1,33 @@ +/** + * \file + * \brief getMallocMutex() declaration + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_MEMORY_GETMALLOCMUTEX_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_MEMORY_GETMALLOCMUTEX_HPP_ + +namespace distortos +{ + +class Mutex; + +namespace internal +{ + +/** + * \return reference to main instance of Mutex used for malloc() and free() locking + */ + +Mutex& getMallocMutex(); + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_MEMORY_GETMALLOCMUTEX_HPP_ diff --git a/include/distortos/internal/memory/storageDeleter.hpp b/include/distortos/internal/memory/storageDeleter.hpp new file mode 100644 index 0000000..da7173f --- /dev/null +++ b/include/distortos/internal/memory/storageDeleter.hpp @@ -0,0 +1,44 @@ +/** + * \file + * \brief storageDeleter() definition + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_MEMORY_STORAGEDELETER_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_MEMORY_STORAGEDELETER_HPP_ + +namespace distortos +{ + +namespace internal +{ + +/*---------------------------------------------------------------------------------------------------------------------+ +| global functions' declarations ++---------------------------------------------------------------------------------------------------------------------*/ + +/** + * \brief Templated deleter that can be used with std::unique_ptr and dynamic storage allocated with new T[]. + * + * \tparam T is the real type of allocated storage + * \tparam U is the type of \a storage pointer + * + * \param [in] storage is a pointer to storage that will be deleted + */ + +template<typename T, typename U> +void storageDeleter(U* const storage) +{ + delete[] reinterpret_cast<T*>(storage); +} + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_MEMORY_STORAGEDELETER_HPP_ diff --git a/include/distortos/internal/scheduler/DynamicThreadBase.hpp b/include/distortos/internal/scheduler/DynamicThreadBase.hpp new file mode 100644 index 0000000..5ca3ff8 --- /dev/null +++ b/include/distortos/internal/scheduler/DynamicThreadBase.hpp @@ -0,0 +1,231 @@ +/** + * \file + * \brief DynamicThreadBase class header + * + * \author Copyright (C) 2015-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_ + +#include "distortos/DynamicSignalsReceiver.hpp" +#include "distortos/DynamicThreadParameters.hpp" +#include "distortos/ThreadCommon.hpp" + +#include "distortos/internal/memory/storageDeleter.hpp" + +namespace distortos +{ + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +class DynamicThread; + +#endif // def CONFIG_THREAD_DETACH_ENABLE + +namespace internal +{ + +/** + * \brief DynamicThreadBase class is a type-erased interface for thread that has dynamic storage for bounded function, + * stack and internal DynamicSignalsReceiver object. + * + * If thread detachment is enabled (CONFIG_THREAD_DETACH_ENABLE is defined) then this class is dynamically allocated by + * DynamicThread - which allows it to be "detached". Otherwise - if thread detachment is disabled + * (CONFIG_THREAD_DETACH_ENABLE is not defined) - DynamicThread just inherits from this class. + */ + +class DynamicThreadBase : public ThreadCommon +{ +public: + +#ifdef CONFIG_THREAD_DETACH_ENABLE + + /** + * \brief DynamicThreadBase's constructor + * + * \tparam Function is the function that will be executed in separate thread + * \tparam Args are the arguments for \a Function + * + * \param [in] stackSize is the size of stack, bytes + * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this + * thread + * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread + * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable catching of signals for this thread + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] owner is a reference to owner DynamicThread object + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + */ + + template<typename Function, typename... Args> + DynamicThreadBase(size_t stackSize, bool canReceiveSignals, size_t queuedSignals, size_t signalActions, + uint8_t priority, SchedulingPolicy schedulingPolicy, DynamicThread& owner, Function&& function, + Args&&... args); + + /** + * \brief Detaches the thread. + * + * Similar to std::thread::detach() - http://en.cppreference.com/w/cpp/thread/thread/detach + * Similar to POSIX pthread_detach() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html + * + * Detaches the executing thread from the Thread object, allowing execution to continue independently. All resources + * allocated for the thread will be deallocated when the thread terminates. + * + * \return 0 on success, error code otherwise: + * - EINVAL - this thread is already detached; + */ + + int detach() override; + +#else // !def CONFIG_THREAD_DETACH_ENABLE + + /** + * \brief DynamicThreadBase's constructor + * + * \tparam Function is the function that will be executed in separate thread + * \tparam Args are the arguments for \a Function + * + * \param [in] stackSize is the size of stack, bytes + * \param [in] canReceiveSignals selects whether reception of signals is enabled (true) or disabled (false) for this + * thread + * \param [in] queuedSignals is the max number of queued signals for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable queuing of signals for this thread + * \param [in] signalActions is the max number of different SignalAction objects for this thread, relevant only if + * \a canReceiveSignals == true, 0 to disable catching of signals for this thread + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + */ + + template<typename Function, typename... Args> + DynamicThreadBase(size_t stackSize, bool canReceiveSignals, size_t queuedSignals, size_t signalActions, + uint8_t priority, SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args); + + /** + * \brief DynamicThreadBase's constructor + * + * \tparam Function is the function that will be executed in separate thread + * \tparam Args are the arguments for \a Function + * + * \param [in] parameters is a DynamicThreadParameters struct with thread parameters + * \param [in] function is a function that will be executed in separate thread + * \param [in] args are arguments for \a function + */ + + template<typename Function, typename... Args> + DynamicThreadBase(const DynamicThreadParameters parameters, Function&& function, Args&&... args) : + DynamicThreadBase{parameters.stackSize, parameters.canReceiveSignals, parameters.queuedSignals, + parameters.signalActions, parameters.priority, parameters.schedulingPolicy, + std::forward<Function>(function), std::forward<Args>(args)...} + { + + } + +#endif // !def CONFIG_THREAD_DETACH_ENABLE + + DynamicThreadBase(const DynamicThreadBase&) = delete; + DynamicThreadBase(DynamicThreadBase&&) = default; + const DynamicThreadBase& operator=(const DynamicThreadBase&) = delete; + DynamicThreadBase& operator=(DynamicThreadBase&&) = delete; + +protected: + +#ifdef CONFIG_THREAD_DETACH_ENABLE + + /** + * \brief Pre-termination hook function of thread + * + * If thread is detached, locks object used for deferred deletion. + * + * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase! + */ + + static void preTerminationHook(Thread& thread); + + /** + * \brief Termination hook function of thread + * + * Calls ThreadCommon::terminationHook() and - if thread is detached - schedules itself for deferred deletion. + * + * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase! + */ + + static void terminationHook(Thread& thread); + +#endif // def CONFIG_THREAD_DETACH_ENABLE + +private: + + /** + * \brief Thread's "run" function. + * + * Executes bound function object. + * + * \param [in] thread is a reference to Thread object, this must be DynamicThreadBase! + */ + + static void run(Thread& thread); + + /// internal DynamicSignalsReceiver object + DynamicSignalsReceiver dynamicSignalsReceiver_; + + /// bound function object + std::function<void()> boundFunction_; + +#ifdef CONFIG_THREAD_DETACH_ENABLE + + /// pointer to owner DynamicThread object, nullptr if thread is detached + DynamicThread* owner_; + +#endif // def CONFIG_THREAD_DETACH_ENABLE +}; + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +template<typename Function, typename... Args> +DynamicThreadBase::DynamicThreadBase(const size_t stackSize, const bool canReceiveSignals, const size_t queuedSignals, + const size_t signalActions, const uint8_t priority, const SchedulingPolicy schedulingPolicy, + DynamicThread& owner, Function&& function, Args&&... args) : + ThreadCommon{{{new uint8_t[stackSize], storageDeleter<uint8_t>}, stackSize, *this, run, + preTerminationHook, terminationHook}, priority, schedulingPolicy, nullptr, + canReceiveSignals == true ? &dynamicSignalsReceiver_ : nullptr}, + dynamicSignalsReceiver_{canReceiveSignals == true ? queuedSignals : 0, + canReceiveSignals == true ? signalActions : 0}, + boundFunction_{std::bind(std::forward<Function>(function), std::forward<Args>(args)...)}, + owner_{&owner} +{ + +} + +#else // !def CONFIG_THREAD_DETACH_ENABLE + +template<typename Function, typename... Args> +DynamicThreadBase::DynamicThreadBase(const size_t stackSize, const bool canReceiveSignals, const size_t queuedSignals, + const size_t signalActions, const uint8_t priority, const SchedulingPolicy schedulingPolicy, + Function&& function, Args&&... args) : + ThreadCommon{{{new uint8_t[stackSize], storageDeleter<uint8_t>}, stackSize, *this, run, nullptr, + terminationHook}, priority, schedulingPolicy, nullptr, + canReceiveSignals == true ? &dynamicSignalsReceiver_ : nullptr}, + dynamicSignalsReceiver_{canReceiveSignals == true ? queuedSignals : 0, + canReceiveSignals == true ? signalActions : 0}, + boundFunction_{std::bind(std::forward<Function>(function), std::forward<Args>(args)...)} +{ + +} + +#endif // !def CONFIG_THREAD_DETACH_ENABLE + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_DYNAMICTHREADBASE_HPP_ diff --git a/include/distortos/internal/scheduler/MainThread.hpp b/include/distortos/internal/scheduler/MainThread.hpp new file mode 100644 index 0000000..2eadab6 --- /dev/null +++ b/include/distortos/internal/scheduler/MainThread.hpp @@ -0,0 +1,46 @@ +/** + * \file + * \brief MainThread class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_MAINTHREAD_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_MAINTHREAD_HPP_ + +#include "distortos/UndetachableThread.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// MainThread class is a Thread for main() +class MainThread : public UndetachableThread +{ +public: + + /** + * \brief MainThread's constructor. + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] threadGroupControlBlock is a reference to ThreadGroupControlBlock to which this object will be added + * \param [in] signalsReceiver is a pointer to SignalsReceiver object for main thread, nullptr to disable reception + * of signals for main thread + */ + + MainThread(uint8_t priority, ThreadGroupControlBlock& threadGroupControlBlock, SignalsReceiver* signalsReceiver); + + using UndetachableThread::getThreadControlBlock; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_MAINTHREAD_HPP_ diff --git a/include/distortos/internal/scheduler/RoundRobinQuantum.hpp b/include/distortos/internal/scheduler/RoundRobinQuantum.hpp new file mode 100644 index 0000000..e0a443e --- /dev/null +++ b/include/distortos/internal/scheduler/RoundRobinQuantum.hpp @@ -0,0 +1,123 @@ +/** + * \file + * \brief RoundRobinQuantum class header + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_ROUNDROBINQUANTUM_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_ROUNDROBINQUANTUM_HPP_ + +#include "distortos/TickClock.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// RoundRobinQuantum class is a quantum of time for round-robin scheduling +class RoundRobinQuantum +{ +public: + + /// type of quantum counter + using Representation = uint8_t; + + /// duration type used for quantum + using Duration = std::chrono::duration<Representation, TickClock::period>; + + /** + * \return initial value for round-robin quantum + */ + + constexpr static Duration getInitial() + { + return Duration{quantumRawInitializer_}; + } + + /** + * \brief RoundRobinQuantum's constructor + * + * Initializes quantum value to initial value - just like after call to reset(). + */ + + constexpr RoundRobinQuantum() : + quantum_{getInitial()} + { + + } + + /** + * \brief Decrements round-robin's quantum. + * + * This function should be called from tick interrupt for the currently running thread. Underflow of quantum after + * this decrement is not possible. + * + * \note this function must be called with enabled interrupt masking + */ + + void decrement() + { + if (isZero() == false) + --quantum_; + } + + /** + * \brief Gets current value of round-robin's quantum. + * + * \return current value of round-robin's quantum of the thread + */ + + Duration get() const + { + return quantum_; + } + + /** + * \brief Convenience function to test whether the quantum is already at 0. + * + * \return true if quantum is zero, false otherwise + */ + + bool isZero() const + { + return quantum_ == Duration{0}; + } + + /** + * \brief Resets value of round-robin's quantum. + * + * This function should be called from context switcher after selecting new task that will be run. + */ + + void reset() + { + quantum_ = getInitial(); + } + +private: + + static_assert(CONFIG_TICK_FREQUENCY > 0, "CONFIG_TICK_FREQUENCY must be positive and non-zero!"); + static_assert(CONFIG_ROUND_ROBIN_FREQUENCY > 0, "CONFIG_ROUND_ROBIN_FREQUENCY must be positive and non-zero!"); + + /// raw initializer value for round-robin quantum, calculated with rounding to nearest + constexpr static auto quantumRawInitializer_ = (CONFIG_TICK_FREQUENCY + CONFIG_ROUND_ROBIN_FREQUENCY / 2) / + CONFIG_ROUND_ROBIN_FREQUENCY; + + static_assert(quantumRawInitializer_ > 0 || quantumRawInitializer_ <= UINT8_MAX, + "CONFIG_TICK_FREQUENCY and CONFIG_ROUND_ROBIN_FREQUENCY values produce invalid round-robin quantum!"); + + /// round-robin quantum + Duration quantum_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_ROUNDROBINQUANTUM_HPP_ diff --git a/include/distortos/internal/scheduler/Scheduler.hpp b/include/distortos/internal/scheduler/Scheduler.hpp new file mode 100644 index 0000000..42037f2 --- /dev/null +++ b/include/distortos/internal/scheduler/Scheduler.hpp @@ -0,0 +1,351 @@ +/** + * \file + * \brief Scheduler class header + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SCHEDULER_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SCHEDULER_HPP_ + +#include "distortos/internal/scheduler/ThreadControlBlock.hpp" +#include "distortos/internal/scheduler/ThreadList.hpp" +#include "distortos/internal/scheduler/SoftwareTimerSupervisor.hpp" + +namespace distortos +{ + +namespace internal +{ + +class MainThread; + +/// Scheduler class is a system's scheduler +class Scheduler +{ +public: + + /** + * \brief Scheduler's constructor + */ + + constexpr Scheduler() : + currentThreadControlBlock_{}, + runnableList_{}, + suspendedList_{}, + softwareTimerSupervisor_{}, + contextSwitchCount_{}, + tickCount_{} + { + + } + + /** + * \brief Adds new ThreadControlBlock to scheduler. + * + * ThreadControlBlock's state is changed to "runnable". + * + * \param [in] threadControlBlock is a reference to added ThreadControlBlock object + * + * \return 0 on success, error code otherwise: + * - EINVAL - thread is already started; + * - error codes returned by Scheduler::addInternal(); + */ + + int add(ThreadControlBlock& threadControlBlock); + + /** + * \brief Blocks current thread, transferring it to provided container. + * + * \param [in] container is a reference to destination container to which the thread will be transferred + * \param [in] state is the new state of thread that will be blocked + * \param [in] unblockFunctor is a pointer to ThreadControlBlock::UnblockFunctor which will be executed in + * ThreadControlBlock::unblockHook(), default - nullptr (no functor will be executed) + * + * \return 0 on success, error code otherwise: + * - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal; + * - ETIMEDOUT - thread was unblocked with ThreadControlBlock::UnblockReason::timeout; + */ + + int block(ThreadList& container, ThreadState state, const ThreadControlBlock::UnblockFunctor* unblockFunctor = {}); + + /** + * \brief Blocks thread, transferring it to provided container. + * + * The thread must be on "runnable" list - trying to block thread in other state is an error. + * + * \param [in] container is a reference to destination container to which the thread will be transferred + * \param [in] iterator is the iterator to the thread that will be blocked + * \param [in] state is the new state of thread that will be blocked + * \param [in] unblockFunctor is a pointer to ThreadControlBlock::UnblockFunctor which will be executed in + * ThreadControlBlock::unblockHook(), default - nullptr (no functor will be executed) + * + * \return 0 on success, error code otherwise: + * - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal (possible only when blocking + * current thread); + * - EINVAL - provided thread is not on "runnable" list; + * - ETIMEDOUT - thread was unblocked with ThreadControlBlock::UnblockReason::timeout (possible only when blocking + * current thread); + */ + + int block(ThreadList& container, ThreadList::iterator iterator, ThreadState state, + const ThreadControlBlock::UnblockFunctor* unblockFunctor = {}); + + /** + * \brief Blocks current thread with timeout, transferring it to provided container. + * + * \param [in] container is a reference to destination container to which the thread will be transferred + * \param [in] state is the new state of thread that will be blocked + * \param [in] timePoint is the time point at which the thread will be unblocked (if not already unblocked) + * \param [in] unblockFunctor is a pointer to ThreadControlBlock::UnblockFunctor which will be executed in + * ThreadControlBlock::unblockHook(), default - nullptr (no functor will be executed) + * + * \return 0 on success, error code otherwise: + * - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal; + * - ETIMEDOUT - thread was unblocked because timePoint was reached; + */ + + int blockUntil(ThreadList& container, ThreadState state, TickClock::time_point timePoint, + const ThreadControlBlock::UnblockFunctor* unblockFunctor = {}); + + /** + * \return number of context switches + */ + + uint64_t getContextSwitchCount() const; + + /** + * \return reference to currently active ThreadControlBlock + */ + + ThreadControlBlock& getCurrentThreadControlBlock() const + { + return *currentThreadControlBlock_; + } + + /** + * \return reference to internal SoftwareTimerSupervisor object + */ + + SoftwareTimerSupervisor& getSoftwareTimerSupervisor() + { + return softwareTimerSupervisor_; + } + + /** + * \return const reference to internal SoftwareTimerSupervisor object + */ + + const SoftwareTimerSupervisor& getSoftwareTimerSupervisor() const + { + return softwareTimerSupervisor_; + } + + /** + * \return current value of tick count + */ + + uint64_t getTickCount() const; + + /** + * \brief Scheduler's initialization + * + * \attention This must be called after constructor, before enabling any scheduling. Priority of main thread must + * be higher than priority of idle thread + * + * \param [in] mainThread is a reference to main thread + * + * \return 0 on success, error code otherwise: + * - error codes returned by Scheduler::addInternal(); + */ + + int initialize(MainThread& mainThread); + + /** + * \brief Requests context switch if it is needed. + * + * \attention This function must be called with interrupt masking enabled. + */ + + void maybeRequestContextSwitch() const; + + /** + * \brief Removes current thread from Scheduler's control. + * + * Thread's state is changed to "terminated". + * + * \note This function must be called with masked interrupts. + * + * \note This function can be used only after thread's function returns an all cleanup is done. + * + * \return 0 on success, error code otherwise: + * - EINVAL - provided thread is not on "runnable" list and cannot be removed/terminated; + */ + + int remove(); + + /** + * \brief Resumes suspended thread. + * + * The thread must be on the "suspended" list - trying to resume thread that is not suspended is an error. + * + * \param [in] iterator is the iterator to the thread that will be resumed + * + * \return 0 on success, error code otherwise: + * - EINVAL - provided thread is not on "suspended" list; + */ + + int resume(ThreadList::iterator iterator); + + /** + * \brief Suspends current thread. + * + * \return 0 on success, error code otherwise: + * - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal; + */ + + int suspend(); + + /** + * \brief Suspends thread. + * + * The thread must be on "runnable" list - trying to suspend thread in other state is an error. + * + * \param [in] iterator is the iterator to the thread that will be suspended + * + * \return 0 on success, error code otherwise: + * - EINTR - thread was unblocked with ThreadControlBlock::UnblockReason::signal; + * - EINVAL - provided thread is not on "runnable" list; + */ + + int suspend(ThreadList::iterator iterator); + + /** + * \brief Called by architecture-specific code to do final context switch. + * + * Current task is suspended and the next available task is started. + * + * \param [in] stackPointer is the current value of current thread's stack pointer + * + * \return new thread's stack pointer + */ + + void* switchContext(void* stackPointer); + + /** + * \brief Handler of "tick" interrupt. + * + * \note this must not be called by user code + * + * \return true if context switch is required, false otherwise + */ + + bool tickInterruptHandler(); + + /** + * \brief Unblocks provided thread, transferring it from it's current container to "runnable" container. + * + * Current container of the thread is obtained with ThreadControlBlock::getList(). + * + * \param [in] iterator is the iterator which points to unblocked thread + * \param [in] unblockReason is the reason of unblocking of the thread, default - + * ThreadControlBlock::UnblockReason::unblockRequest + */ + + void unblock(ThreadList::iterator iterator, + ThreadControlBlock::UnblockReason unblockReason = ThreadControlBlock::UnblockReason::unblockRequest); + + /** + * \brief Yields time slot of the scheduler to next thread. + */ + + void yield(); + +private: + + /** + * \brief Adds new ThreadControlBlock to scheduler. + * + * Internal version - without interrupt masking and call to Scheduler::maybeRequestContextSwitch() + * + * \param [in] threadControlBlock is a reference to added ThreadControlBlock object + * + * \return 0 on success, error code otherwise: + * - error codes returned by ThreadControlBlock::addHook(); + */ + + int addInternal(ThreadControlBlock& threadControlBlock); + + /** + * \brief Blocks thread, transferring it to provided container. + * + * Internal version - without interrupt masking and forced context switch. + * + * \param [in] container is a reference to destination container to which the thread will be transferred + * \param [in] iterator is the iterator to the thread that will be blocked + * \param [in] state is the new state of thread that will be blocked + * \param [in] unblockFunctor is a pointer to ThreadControlBlock::UnblockFunctor which will be executed in + * ThreadControlBlock::unblockHook() + * + * \return 0 on success, error code otherwise: + * - EINVAL - provided thread is not on "runnable" list; + */ + + int blockInternal(ThreadList& container, ThreadList::iterator iterator, ThreadState state, + const ThreadControlBlock::UnblockFunctor* unblockFunctor); + + /** + * \brief Tests whether context switch is required or not. + * + * Context switch is required in following situations: + * - current thread is no longer on "runnable" list, + * - current thread is no longer on the beginning of the "runnable" list (because higher-priority thread is + * available or current thread was "rotated" due to round-robin scheduling policy). + * + * \return true if context switch is required + */ + + bool isContextSwitchRequired() const; + + /** + * \brief Unblocks provided thread, transferring it from it's current container to "runnable" container. + * + * Current container of the thread is obtained with ThreadControlBlock::getList(). Round-robin quantum of thread is + * reset. + * + * \note Internal version - without interrupt masking and yield() + * + * \param [in] iterator is the iterator which points to unblocked thread + * \param [in] unblockReason is the reason of unblocking of the thread + */ + + void unblockInternal(ThreadList::iterator iterator, ThreadControlBlock::UnblockReason unblockReason); + + /// iterator to the currently active ThreadControlBlock + ThreadList::iterator currentThreadControlBlock_; + + /// list of ThreadControlBlock elements in "runnable" state, sorted by priority in descending order + ThreadList runnableList_; + + /// list of ThreadControlBlock elements in "suspended" state, sorted by priority in descending order + ThreadList suspendedList_; + + /// internal SoftwareTimerSupervisor object + SoftwareTimerSupervisor softwareTimerSupervisor_; + + /// number of context switches + uint64_t contextSwitchCount_; + + /// tick count + uint64_t tickCount_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SCHEDULER_HPP_ diff --git a/include/distortos/internal/scheduler/SoftwareTimerControlBlock.hpp b/include/distortos/internal/scheduler/SoftwareTimerControlBlock.hpp new file mode 100644 index 0000000..c020d35 --- /dev/null +++ b/include/distortos/internal/scheduler/SoftwareTimerControlBlock.hpp @@ -0,0 +1,111 @@ +/** + * \file + * \brief SoftwareTimerControlBlock class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERCONTROLBLOCK_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERCONTROLBLOCK_HPP_ + +#include "distortos/internal/scheduler/SoftwareTimerListNode.hpp" + +namespace distortos +{ + +class SoftwareTimer; + +namespace internal +{ + +/// SoftwareTimerControlBlock class is a control block of software timer +class SoftwareTimerControlBlock : public SoftwareTimerListNode +{ +public: + + /// type of runner for software timer's function + using FunctionRunner = void(SoftwareTimer&); + + /** + * \brief SoftwareTimerControlBlock's constructor + * + * \param [in] functionRunner is a reference to runner for software timer's function + * \param [in] owner is a reference to SoftwareTimer object that owns this SoftwareTimerControlBlock + */ + + constexpr SoftwareTimerControlBlock(FunctionRunner& functionRunner, SoftwareTimer& owner) : + SoftwareTimerListNode{}, + functionRunner_{functionRunner}, + owner_{owner} + { + + } + + /** + * \brief SoftwareTimerControlBlock's destructor + * + * If the timer is running it is stopped. + */ + + ~SoftwareTimerControlBlock() + { + stop(); + } + + /** + * \return true if the timer is running, false otherwise + */ + + bool isRunning() const + { + return node.isLinked(); + } + + /** + * \brief Runs software timer's function. + * + * \note this should only be called by SoftwareTimerSupervisor::tickInterruptHandler() + */ + + void run() const + { + functionRunner_(owner_); + } + + /** + * \brief Starts the timer. + * + * \param [in] timePoint is the time point at which the function will be executed + */ + + void start(TickClock::time_point timePoint); + + /** + * \brief Stops the timer. + */ + + void stop(); + + SoftwareTimerControlBlock(const SoftwareTimerControlBlock&) = delete; + SoftwareTimerControlBlock(SoftwareTimerControlBlock&&) = default; + const SoftwareTimerControlBlock& operator=(const SoftwareTimerControlBlock&) = delete; + SoftwareTimerControlBlock& operator=(SoftwareTimerControlBlock&&) = delete; + +private: + + /// reference to runner for software timer's function + FunctionRunner& functionRunner_; + + /// reference to SoftwareTimer object that owns this SoftwareTimerControlBlock + SoftwareTimer& owner_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERCONTROLBLOCK_HPP_ diff --git a/include/distortos/internal/scheduler/SoftwareTimerList.hpp b/include/distortos/internal/scheduler/SoftwareTimerList.hpp new file mode 100644 index 0000000..5d38f47 --- /dev/null +++ b/include/distortos/internal/scheduler/SoftwareTimerList.hpp @@ -0,0 +1,62 @@ +/** + * \file + * \brief SoftwareTimerList class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERLIST_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERLIST_HPP_ + +#include "distortos/internal/scheduler/SoftwareTimerListNode.hpp" + +#include "estd/SortedIntrusiveList.hpp" + +namespace distortos +{ + +namespace internal +{ + +class SoftwareTimerControlBlock; + +/// functor which gives ascending expiration time point order of elements on the list +struct SoftwareTimerAscendingTimePoint +{ + /** + * \brief SoftwareTimerAscendingTimePoint's constructor + */ + + constexpr SoftwareTimerAscendingTimePoint() + { + + } + + /** + * \brief SoftwareTimerAscendingTimePoint's function call operator + * + * \param [in] left is the object on the left side of comparison + * \param [in] right is the object on the right side of comparison + * + * \return true if left's expiration time point is greater than right's expiration time point + */ + + bool operator()(const SoftwareTimerListNode& left, const SoftwareTimerListNode& right) const + { + return left.getTimePoint() > right.getTimePoint(); + } +}; + +/// sorted intrusive list of software timers (software timer control blocks) +using SoftwareTimerList = estd::SortedIntrusiveList<SoftwareTimerAscendingTimePoint, SoftwareTimerListNode, + &SoftwareTimerListNode::node, SoftwareTimerControlBlock>; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERLIST_HPP_ diff --git a/include/distortos/internal/scheduler/SoftwareTimerListNode.hpp b/include/distortos/internal/scheduler/SoftwareTimerListNode.hpp new file mode 100644 index 0000000..299e078 --- /dev/null +++ b/include/distortos/internal/scheduler/SoftwareTimerListNode.hpp @@ -0,0 +1,82 @@ +/** + * \file + * \brief SoftwareTimerListNode class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERLISTNODE_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERLISTNODE_HPP_ + +#include "distortos/TickClock.hpp" + +#include "estd/IntrusiveList.hpp" + +namespace distortos +{ + +namespace internal +{ + +/** + * \brief SoftwareTimerListNode class is a base for SoftwareTimerControlBlock that serves as a node in intrusive list of + * software timers (software timer control blocks) + * + * This class is needed to break any potential circular dependencies. + */ + +class SoftwareTimerListNode +{ +public: + + /** + * \brief SoftwareTimerListNode's constructor + */ + + constexpr SoftwareTimerListNode() : + node{}, + timePoint_{} + { + + } + + /** + * \return const reference to expiration time point + */ + + const TickClock::time_point& getTimePoint() const + { + return timePoint_; + } + + /// node for intrusive list + estd::IntrusiveListNode node; + +protected: + + /** + * \brief Sets time point of expiration + * + * \param [in] timePoint is the new time point of expiration + */ + + void setTimePoint(const TickClock::time_point timePoint) + { + timePoint_ = timePoint; + } + +private: + + ///time point of expiration + TickClock::time_point timePoint_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERLISTNODE_HPP_ diff --git a/include/distortos/internal/scheduler/SoftwareTimerSupervisor.hpp b/include/distortos/internal/scheduler/SoftwareTimerSupervisor.hpp new file mode 100644 index 0000000..8367a0a --- /dev/null +++ b/include/distortos/internal/scheduler/SoftwareTimerSupervisor.hpp @@ -0,0 +1,66 @@ +/** + * \file + * \brief SoftwareTimerSupervisor class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERSUPERVISOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERSUPERVISOR_HPP_ + +#include "distortos/internal/scheduler/SoftwareTimerList.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// SoftwareTimerSupervisor class is a supervisor of software timers +class SoftwareTimerSupervisor +{ +public: + + /** + * \brief SoftwareTimerControlBlock's constructor + */ + + constexpr SoftwareTimerSupervisor() : + activeList_{} + { + + } + + /** + * \brief Adds SoftwareTimerControlBlock to supervisor, effectively starting the software timer. + * + * \param [in] softwareTimerControlBlock is the SoftwareTimerControlBlock being added/started + */ + + void add(SoftwareTimerControlBlock& softwareTimerControlBlock); + + /** + * \brief Handler of "tick" interrupt. + * + * \note this must not be called by user code + * + * \param [in] timePoint is the current time point + */ + + void tickInterruptHandler(TickClock::time_point timePoint); + +private: + + /// list of active software timers (waiting for execution) + SoftwareTimerList activeList_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_SOFTWARETIMERSUPERVISOR_HPP_ diff --git a/include/distortos/internal/scheduler/ThreadControlBlock.hpp b/include/distortos/internal/scheduler/ThreadControlBlock.hpp new file mode 100644 index 0000000..6d6f458 --- /dev/null +++ b/include/distortos/internal/scheduler/ThreadControlBlock.hpp @@ -0,0 +1,336 @@ +/** + * \file + * \brief ThreadControlBlock class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADCONTROLBLOCK_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADCONTROLBLOCK_HPP_ + +#include "distortos/internal/scheduler/RoundRobinQuantum.hpp" +#include "distortos/internal/scheduler/ThreadListNode.hpp" + +#include "distortos/internal/synchronization/MutexList.hpp" + +#include "distortos/architecture/Stack.hpp" + +#include "distortos/SchedulingPolicy.hpp" +#include "distortos/ThreadState.hpp" + +#include "estd/TypeErasedFunctor.hpp" + +namespace distortos +{ + +class SignalsReceiver; + +namespace internal +{ + +class SignalsReceiverControlBlock; +class ThreadList; +class ThreadGroupControlBlock; + +/// ThreadControlBlock class is a simple description of a Thread +class ThreadControlBlock : public ThreadListNode +{ +public: + + /// reason of thread unblocking + enum class UnblockReason : uint8_t + { + /// explicit request to unblock the thread - normal unblock + unblockRequest, + /// timeout - unblock via software timer + timeout, + /// signal handler - unblock to deliver unmasked signal + signal, + }; + + /// UnblockFunctor is a functor executed when unblocking the thread, it receives two parameter - a reference to + /// ThreadControlBlock that is being unblocked and the reason of thread unblocking + class UnblockFunctor : public estd::TypeErasedFunctor<void(ThreadControlBlock&, UnblockReason)> + { + + }; + + /** + * \brief ThreadControlBlock constructor. + * + * \param [in] stack is an rvalue reference to architecture::Stack object which will be adopted for this thread + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + * \param [in] schedulingPolicy is the scheduling policy of the thread + * \param [in] threadGroupControlBlock is a pointer to ThreadGroupControlBlock to which this object will be added, + * nullptr to inherit thread group from currently running thread + * \param [in] signalsReceiver is a pointer to SignalsReceiver object for this thread, nullptr to disable reception + * of signals for this thread + * \param [in] owner is a reference to Thread object that owns this ThreadControlBlock + */ + + ThreadControlBlock(architecture::Stack&& stack, uint8_t priority, SchedulingPolicy schedulingPolicy, + ThreadGroupControlBlock* threadGroupControlBlock, SignalsReceiver* signalsReceiver, Thread& owner); + + /** + * \brief ThreadControlBlock's destructor + */ + + ~ThreadControlBlock(); + + /** + * \brief Hook function executed when thread is added to scheduler. + * + * If threadGroupControlBlock_ is nullptr, it is inherited from currently running thread. Then this object is added + * to the thread group (if it is valid). + * + * \attention This function should be called only by Scheduler::addInternal(). + * + * \return 0 on success, error code otherwise: + * - EINVAL - inherited thread group is invalid; + */ + + int addHook(); + + /** + * \brief Block hook function of thread + * + * Saves pointer to UnblockFunctor. + * + * \attention This function should be called only by Scheduler::blockInternal(). + * + * \param [in] unblockFunctor is a pointer to UnblockFunctor which will be executed in unblockHook() + */ + + void blockHook(const UnblockFunctor* const unblockFunctor) + { + unblockFunctor_ = unblockFunctor; + } + + /** + * \return pointer to list that has this object + */ + + ThreadList* getList() const + { + return list_; + } + + /** + * \return reference to list of mutexes (mutex control blocks) with enabled priority protocol owned by this thread + */ + + MutexList& getOwnedProtocolMutexList() + { + return ownedProtocolMutexList_; + } + + /** + * \return reference to Thread object that owns this ThreadControlBlock + */ + + Thread& getOwner() const + { + return owner_; + } + + /** + * \return reference to internal RoundRobinQuantum object + */ + + RoundRobinQuantum& getRoundRobinQuantum() + { + return roundRobinQuantum_; + } + + /** + * \return scheduling policy of the thread + */ + + SchedulingPolicy getSchedulingPolicy() const + { + return schedulingPolicy_; + } + + /** + * \return pointer to SignalsReceiverControlBlock object for this thread, nullptr if this thread cannot receive + * signals + */ + + SignalsReceiverControlBlock* getSignalsReceiverControlBlock() const + { + return signalsReceiverControlBlock_; + } + + /** + * \return reference to internal Stack object + */ + + architecture::Stack& getStack() + { + return stack_; + } + + /** + * \return current state of object + */ + + ThreadState getState() const + { + return state_; + } + + /** + * \brief Sets the list that has this object. + * + * \param [in] list is a pointer to list that has this object + */ + + void setList(ThreadList* const list) + { + list_ = list; + } + + /** + * \brief Changes priority of thread. + * + * If the priority really changes, the position in the thread list is adjusted and context switch may be requested. + * + * \param [in] priority is the new priority of thread + * \param [in] alwaysBehind selects the method of ordering when lowering the priority + * - false - the thread is moved to the head of the group of threads with the new priority (default), + * - true - the thread is moved to the tail of the group of threads with the new priority. + */ + + void setPriority(uint8_t priority, bool alwaysBehind = {}); + + /** + * \param [in] priorityInheritanceMutexControlBlock is a pointer to MutexControlBlock (with PriorityInheritance + * protocol) that blocks this thread + */ + + void setPriorityInheritanceMutexControlBlock(const MutexControlBlock* const priorityInheritanceMutexControlBlock) + { + priorityInheritanceMutexControlBlock_ = priorityInheritanceMutexControlBlock; + } + + /** + * param [in] schedulingPolicy is the new scheduling policy of the thread + */ + + void setSchedulingPolicy(SchedulingPolicy schedulingPolicy); + + /** + * \param [in] state is the new state of object + */ + + void setState(const ThreadState state) + { + state_ = state; + } + + /** + * \brief Hook function called when context is switched to this thread. + * + * Sets global _impure_ptr (from newlib) to thread's \a reent_ member variable. + * + * \attention This function should be called only by Scheduler::switchContext(). + */ + + void switchedToHook() + { + _impure_ptr = &reent_; + } + + /** + * \brief Unblock hook function of thread + * + * Resets round-robin's quantum and executes unblock functor saved in blockHook(). + * + * \attention This function should be called only by Scheduler::unblockInternal(). + * + * \param [in] unblockReason is the new reason of unblocking of the thread + */ + + void unblockHook(UnblockReason unblockReason); + + /** + * \brief Updates boosted priority of the thread. + * + * This function should be called after all operations involving this thread and a mutex with enabled priority + * protocol. + * + * \param [in] boostedPriority is the initial boosted priority, this should be effective priority of the thread that + * is about to be blocked on a mutex owned by this thread, default - 0 + */ + + void updateBoostedPriority(uint8_t boostedPriority = {}); + + ThreadControlBlock(const ThreadControlBlock&) = delete; + ThreadControlBlock(ThreadControlBlock&&) = default; + const ThreadControlBlock& operator=(const ThreadControlBlock&) = delete; + ThreadControlBlock& operator=(ThreadControlBlock&&) = delete; + +private: + + /** + * \brief Repositions the thread on the list it's currently on. + * + * This function should be called when thread's effective priority changes. + * + * \attention list_ must not be nullptr + * + * \param [in] loweringBefore selects the method of ordering when lowering the priority (it must be false when the + * priority is raised!): + * - true - the thread is moved to the head of the group of threads with the new priority, this is accomplished by + * temporarily boosting effective priority by 1, + * - false - the thread is moved to the tail of the group of threads with the new priority. + */ + + void reposition(bool loweringBefore); + + /// internal stack object + architecture::Stack stack_; + + /// reference to Thread object that owns this ThreadControlBlock + Thread& owner_; + + /// list of mutexes (mutex control blocks) with enabled priority protocol owned by this thread + MutexList ownedProtocolMutexList_; + + /// pointer to MutexControlBlock (with PriorityInheritance protocol) that blocks this thread + const MutexControlBlock* priorityInheritanceMutexControlBlock_; + + /// pointer to list that has this object + ThreadList* list_; + + /// pointer to ThreadGroupControlBlock with which this object is associated + ThreadGroupControlBlock* threadGroupControlBlock_; + + /// functor executed in unblockHook() + const UnblockFunctor* unblockFunctor_; + + /// pointer to SignalsReceiverControlBlock object for this thread, nullptr if this thread cannot receive signals + SignalsReceiverControlBlock* signalsReceiverControlBlock_; + + /// newlib's _reent structure with thread-specific data + _reent reent_; + + /// round-robin quantum + RoundRobinQuantum roundRobinQuantum_; + + /// scheduling policy of the thread + SchedulingPolicy schedulingPolicy_; + + /// current state of object + ThreadState state_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADCONTROLBLOCK_HPP_ diff --git a/include/distortos/internal/scheduler/ThreadGroupControlBlock.hpp b/include/distortos/internal/scheduler/ThreadGroupControlBlock.hpp new file mode 100644 index 0000000..59ff313 --- /dev/null +++ b/include/distortos/internal/scheduler/ThreadGroupControlBlock.hpp @@ -0,0 +1,61 @@ +/** + * \file + * \brief ThreadGroupControlBlock class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADGROUPCONTROLBLOCK_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADGROUPCONTROLBLOCK_HPP_ + +#include "distortos/internal/scheduler/ThreadListNode.hpp" + +namespace distortos +{ + +namespace internal +{ + +class ThreadControlBlock; + +/// ThreadGroupControlBlock class is a control block for ThreadGroup +class ThreadGroupControlBlock +{ +public: + + /** + * \brief ThreadGroupControlBlock's constructor + */ + + constexpr ThreadGroupControlBlock() : + threadList_{} + { + + } + + /** + * \brief Adds new ThreadControlBlock to internal list of this object. + * + * \param [in] threadControlBlock is a reference to added ThreadControlBlock object + */ + + void add(ThreadControlBlock& threadControlBlock); + +private: + + /// intrusive list of threads (thread control blocks) + using List = estd::IntrusiveList<ThreadListNode, &ThreadListNode::threadGroupNode, ThreadControlBlock>; + + /// list of threads (thread control blocks) in this group + List threadList_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADGROUPCONTROLBLOCK_HPP_ diff --git a/include/distortos/internal/scheduler/ThreadList.hpp b/include/distortos/internal/scheduler/ThreadList.hpp new file mode 100644 index 0000000..edf2483 --- /dev/null +++ b/include/distortos/internal/scheduler/ThreadList.hpp @@ -0,0 +1,65 @@ +/** + * \file + * \brief ThreadList class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADLIST_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADLIST_HPP_ + +#include "distortos/internal/scheduler/ThreadListNode.hpp" + +#include "estd/SortedIntrusiveList.hpp" + +namespace distortos +{ + +namespace internal +{ + +class ThreadControlBlock; + +/// functor which gives descending effective priority order of elements on the list +struct ThreadDescendingEffectivePriority +{ + /** + * \brief ThreadDescendingEffectivePriority's constructor + */ + + constexpr ThreadDescendingEffectivePriority() + { + + } + + /** + * \brief ThreadDescendingEffectivePriority's function call operator + * + * \param [in] left is the object on the left-hand side of comparison + * \param [in] right is the object on the right-hand side of comparison + * + * \return true if left's effective priority is less than right's effective priority + */ + + bool operator()(const ThreadListNode& left, const ThreadListNode& right) const + { + return left.getEffectivePriority() < right.getEffectivePriority(); + } +}; + +/// sorted intrusive list of threads (thread control blocks) +class ThreadList : public estd::SortedIntrusiveList<ThreadDescendingEffectivePriority, ThreadListNode, + &ThreadListNode::threadListNode, ThreadControlBlock> +{ + +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADLIST_HPP_ diff --git a/include/distortos/internal/scheduler/ThreadListNode.hpp b/include/distortos/internal/scheduler/ThreadListNode.hpp new file mode 100644 index 0000000..fcd7c85 --- /dev/null +++ b/include/distortos/internal/scheduler/ThreadListNode.hpp @@ -0,0 +1,86 @@ +/** + * \file + * \brief ThreadListNode class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADLISTNODE_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADLISTNODE_HPP_ + +#include "estd/IntrusiveList.hpp" + +namespace distortos +{ + +namespace internal +{ + +/** + * \brief ThreadListNode class is a base for ThreadControlBlock that provides nodes for intrusive lists + * + * This class is needed to break circular dependency - MutexList is contained in ThreadControlBlock and ThreadList is + * contained in MutexControlBlock. + */ + +class ThreadListNode +{ +public: + + /** + * \brief ThreadListNode's constructor + * + * \param [in] priority is the thread's priority, 0 - lowest, UINT8_MAX - highest + */ + + constexpr ThreadListNode(const uint8_t priority) : + threadListNode{}, + threadGroupNode{}, + priority_{priority}, + boostedPriority_{} + { + + } + + /** + * \return effective priority of thread + */ + + uint8_t getEffectivePriority() const + { + return std::max(priority_, boostedPriority_); + } + + /** + * \return priority of thread + */ + + uint8_t getPriority() const + { + return priority_; + } + + /// node for intrusive list in thread lists + estd::IntrusiveListNode threadListNode; + + /// node for intrusive list in thread group + estd::IntrusiveListNode threadGroupNode; + +protected: + + /// thread's priority, 0 - lowest, UINT8_MAX - highest + uint8_t priority_; + + /// thread's boosted priority, 0 - no boosting + uint8_t boostedPriority_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADLISTNODE_HPP_ diff --git a/include/distortos/internal/scheduler/forceContextSwitch.hpp b/include/distortos/internal/scheduler/forceContextSwitch.hpp new file mode 100644 index 0000000..4509e51 --- /dev/null +++ b/include/distortos/internal/scheduler/forceContextSwitch.hpp @@ -0,0 +1,33 @@ +/** + * \file + * \brief forceContextSwitch() declaration + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_FORCECONTEXTSWITCH_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_FORCECONTEXTSWITCH_HPP_ + +namespace distortos +{ + +namespace internal +{ + +/** + * \brief Forces unconditional context switch. + * + * Requests unconditional context switch and temporarily disables any interrupt masking. + */ + +void forceContextSwitch(); + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_FORCECONTEXTSWITCH_HPP_ diff --git a/include/distortos/internal/scheduler/getScheduler.hpp b/include/distortos/internal/scheduler/getScheduler.hpp new file mode 100644 index 0000000..f95d51c --- /dev/null +++ b/include/distortos/internal/scheduler/getScheduler.hpp @@ -0,0 +1,33 @@ +/** + * \file + * \brief getScheduler() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_GETSCHEDULER_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_GETSCHEDULER_HPP_ + +namespace distortos +{ + +namespace internal +{ + +class Scheduler; + +/** + * \return reference to main instance of system's Scheduler + */ + +Scheduler& getScheduler(); + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_GETSCHEDULER_HPP_ diff --git a/include/distortos/internal/scheduler/idleThreadFunction.hpp b/include/distortos/internal/scheduler/idleThreadFunction.hpp new file mode 100644 index 0000000..23699d4 --- /dev/null +++ b/include/distortos/internal/scheduler/idleThreadFunction.hpp @@ -0,0 +1,31 @@ +/** + * \file + * \brief idleThreadFunction() declaration + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_IDLETHREADFUNCTION_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_IDLETHREADFUNCTION_HPP_ + +namespace distortos +{ + +namespace internal +{ + +/** + * \brief Idle thread's function + */ + +void idleThreadFunction(); + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_IDLETHREADFUNCTION_HPP_ diff --git a/include/distortos/internal/scheduler/lowLevelInitialization.hpp b/include/distortos/internal/scheduler/lowLevelInitialization.hpp new file mode 100644 index 0000000..a6298c2 --- /dev/null +++ b/include/distortos/internal/scheduler/lowLevelInitialization.hpp @@ -0,0 +1,44 @@ +/** + * \file + * \brief internal::lowLevelInitialization() declaration + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_LOWLEVELINITIALIZATION_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_LOWLEVELINITIALIZATION_HPP_ + +namespace distortos +{ + +namespace internal +{ + +/*---------------------------------------------------------------------------------------------------------------------+ +| global functions' declarations ++---------------------------------------------------------------------------------------------------------------------*/ + +/** + * \brief Low level system initialization + * + * 1. Initializes main instance of system's Scheduler; + * 2. Initializes main thread with its group; + * 3. Starts idle thread; + * 4. Initializes main instance of Mutex used for malloc() and free() locking; + * 5. Initializes main instance of DeferredThreadDeleter (only if CONFIG_THREAD_DETACH_ENABLE option is enabled); + * + * This function is called before constructors for global and static objects from __libc_init_array() via address in + * distortosPreinitArray[]. + */ + +void lowLevelInitialization(); + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_LOWLEVELINITIALIZATION_HPP_ diff --git a/include/distortos/internal/scheduler/threadRunner.hpp b/include/distortos/internal/scheduler/threadRunner.hpp new file mode 100644 index 0000000..1cee654 --- /dev/null +++ b/include/distortos/internal/scheduler/threadRunner.hpp @@ -0,0 +1,48 @@ +/** + * \file + * \brief threadRunner() declaration + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADRUNNER_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADRUNNER_HPP_ + +namespace distortos +{ + +class Thread; + +namespace internal +{ + +/** + * \brief Thread runner function - entry point of threads. + * + * Performs following actions: + * - executes thread's "run" function; + * - thread's pre-termination hook is executed (if provided); + * - thread is terminated and removed from scheduler; + * - thread's termination hook is executed; + * - context switch is forced; + * + * This function never returns. + * + * \param [in] thread is a reference to Thread object that is being run + * \param [in] run is a reference to Thread's "run" function + * \param [in] preTerminationHook is a pointer to Thread's pre-termination hook, nullptr to skip + * \param [in] terminationHook is a reference to Thread's termination hook + */ + +void threadRunner(Thread& thread, void (& run)(Thread&), void (* preTerminationHook)(Thread&), + void (& terminationHook)(Thread&)) __attribute__ ((noreturn)); + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SCHEDULER_THREADRUNNER_HPP_ diff --git a/include/distortos/internal/synchronization/BoundQueueFunctor.hpp b/include/distortos/internal/synchronization/BoundQueueFunctor.hpp new file mode 100644 index 0000000..35a9d14 --- /dev/null +++ b/include/distortos/internal/synchronization/BoundQueueFunctor.hpp @@ -0,0 +1,89 @@ +/** + * \file + * \brief BoundQueueFunctor class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_BOUNDQUEUEFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_BOUNDQUEUEFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/QueueFunctor.hpp" + +#include <utility> + +namespace distortos +{ + +namespace internal +{ + +/** + * \brief BoundQueueFunctor is a type-erased QueueFunctor which calls its bound functor to execute actions on queue's + * storage + * + * \tparam F is the type of bound functor, it will be called with <em>void*</em> as only argument + */ + +template<typename F> +class BoundQueueFunctor : public QueueFunctor +{ +public: + + /** + * \brief BoundQueueFunctor's constructor + * + * \param [in] boundFunctor is a rvalue reference to bound functor which will be used to move-construct internal + * bound functor + */ + + constexpr explicit BoundQueueFunctor(F&& boundFunctor) : + boundFunctor_{std::move(boundFunctor)} + { + + } + + /** + * \brief Calls the bound functor which will execute some action on queue's storage (like copy-constructing, + * swapping, destroying, emplacing, ...) + * + * \param [in,out] storage is a pointer to storage with/for element + */ + + void operator()(void* const storage) const override + { + boundFunctor_(storage); + } + +private: + + /// bound functor + F boundFunctor_; +}; + +/** + * \brief Helper factory function to make BoundQueueFunctor object with deduced template arguments + * + * \tparam F is the type of bound functor, it will be called with <em>void*</em> as only argument + * + * \param [in] boundFunctor is a rvalue reference to bound functor which will be used to move-construct internal bound + * functor + * + * \return BoundQueueFunctor object with deduced template arguments + */ + +template<typename F> +constexpr BoundQueueFunctor<F> makeBoundQueueFunctor(F&& boundFunctor) +{ + return BoundQueueFunctor<F>{std::move(boundFunctor)}; +} + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_BOUNDQUEUEFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/CallOnceControlBlock.hpp b/include/distortos/internal/synchronization/CallOnceControlBlock.hpp new file mode 100644 index 0000000..1684faf --- /dev/null +++ b/include/distortos/internal/synchronization/CallOnceControlBlock.hpp @@ -0,0 +1,172 @@ +/** + * \file + * \brief CallOnceControlBlock class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_ + +#include "estd/invoke.hpp" +#include "estd/TypeErasedFunctor.hpp" + +#include <sys/features.h> + +namespace distortos +{ + +/// GCC 4.9 is needed for CallOnceControlBlock::operator()() function - and thus for OnceFlag and callOnce() - earlier +/// versions don't support parameter pack expansion in lambdas +#define DISTORTOS_CALLONCE_SUPPORTED __GNUC_PREREQ(4, 9) + +#if DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1 + +namespace internal +{ + +class ThreadList; + +/// CallOnceControlBlock class implements functionality of OnceFlag class and callOnce() +/// \note This class requires GCC 4.9. +class CallOnceControlBlock +{ +public: + + /** + * \brief CallOnceControlBlock's constructor + */ + + constexpr CallOnceControlBlock() : + blockedList_{}, + done_{} + { + + } + + /** + * \brief CallOnceControlBlock's function call operator + * + * Does nothing if any function was already called for this object. In other case provided function and arguments + * are wrapped in a type-erased functor and passed to callOnceImplementation(). + * + * \tparam Function is the function object that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] function is the function object that will be executed + * \param [in] args are arguments for \a function + */ + + template<typename Function, typename... Args> + void operator()(Function&& function, Args&&... args); + +private: + + /// Functor is a type-erased interface for functors which execute bounded function with bounded arguments + class Functor : public estd::TypeErasedFunctor<void()> + { + + }; + + /** + * \brief BoundedFunctor is a type-erased Functor which calls its bounded functor + * + * \tparam F is the type of bounded functor + */ + + template<typename F> + class BoundedFunctor : public Functor + { + public: + + /** + * \brief BoundedFunctor's constructor + * + * \param [in] boundedFunctor is a rvalue reference to bounded functor which will be used to move-construct + * internal bounded functor + */ + + constexpr explicit BoundedFunctor(F&& boundedFunctor) : + boundedFunctor_{std::move(boundedFunctor)} + { + + } + + /** + * \brief BoundedFunctor's function call operator + * + * Calls the bounded functor. + */ + + void operator()() const override + { + boundedFunctor_(); + } + + private: + + /// bounded functor + F boundedFunctor_; + }; + + /** + * \brief Helper factory function to make BoundedFunctor object with deduced template arguments + * + * \tparam F is the type of bounded functor + * + * \param [in] boundedFunctor is a rvalue reference to bounded functor which will be used to move-construct internal + * bounded functor + * + * \return BoundedFunctor object with deduced template arguments + */ + + template<typename F> + constexpr static BoundedFunctor<F> makeBoundedFunctor(F&& boundedFunctor) + { + return BoundedFunctor<F>{std::move(boundedFunctor)}; + } + + /** + * \brief Implements callOnce() using type-erased functor. + * + * Does nothing if any function was already called for this object. If the function is currently being executed, but + * not yet done, then the calling thread is blocked. In other case the function is executed and - after it is done - + * all blocked threads are unblocked. + * + * \param [in] functor is a reference to functor which will execute bounded function with bounded arguments + */ + + void callOnceImplementation(const Functor& functor); + + /// pointer to stack-allocated list of ThreadControlBlock objects blocked on associated OnceFlag + ThreadList* blockedList_; + + /// tells whether any function was already called for this object (true) or not (false) + bool done_; +}; + +template<typename Function, typename... Args> +void CallOnceControlBlock::operator()(Function&& function, Args&&... args) +{ + if (done_ == true) // function already executed? + return; + + const auto functor = makeBoundedFunctor( + [&function, &args...]() + { + estd::invoke(std::forward<Function>(function), std::forward<Args>(args)...); + }); + callOnceImplementation(functor); +} + +} // namespace internal + +#endif // DISTORTOS_CALLONCE_SUPPORTED == 1 || DOXYGEN == 1 + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_CALLONCECONTROLBLOCK_HPP_ diff --git a/include/distortos/internal/synchronization/CopyConstructQueueFunctor.hpp b/include/distortos/internal/synchronization/CopyConstructQueueFunctor.hpp new file mode 100644 index 0000000..7c29c8a --- /dev/null +++ b/include/distortos/internal/synchronization/CopyConstructQueueFunctor.hpp @@ -0,0 +1,67 @@ +/** + * \file + * \brief CopyConstructQueueFunctor class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_COPYCONSTRUCTQUEUEFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_COPYCONSTRUCTQUEUEFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/QueueFunctor.hpp" + +namespace distortos +{ + +namespace internal +{ + +/** + * CopyConstructQueueFunctor is a functor used for pushing of data to the queue using copy-construction + * + * \tparam T is the type of data pushed to the queue + */ + +template<typename T> +class CopyConstructQueueFunctor : public QueueFunctor +{ +public: + + /** + * \brief CopyConstructQueueFunctor's constructor + * + * \param [in] value is a reference to object that will be used as argument of copy constructor + */ + + constexpr explicit CopyConstructQueueFunctor(const T& value) : + value_{value} + { + + } + + /** + * \brief Copy-constructs the element in the queue's storage + * + * \param [in,out] storage is a pointer to storage for element + */ + + void operator()(void* const storage) const override + { + new (storage) T{value_}; + } + +private: + + /// reference to object that will be used as argument of copy constructor + const T& value_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_COPYCONSTRUCTQUEUEFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/FifoQueueBase.hpp b/include/distortos/internal/synchronization/FifoQueueBase.hpp new file mode 100644 index 0000000..f45eec8 --- /dev/null +++ b/include/distortos/internal/synchronization/FifoQueueBase.hpp @@ -0,0 +1,145 @@ +/** + * \file + * \brief FifoQueueBase class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_FIFOQUEUEBASE_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_FIFOQUEUEBASE_HPP_ + +#include "distortos/Semaphore.hpp" + +#include "distortos/internal/synchronization/QueueFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreFunctor.hpp" + +#include <memory> + +namespace distortos +{ + +namespace internal +{ + +/// FifoQueueBase class implements basic functionality of FifoQueue template class +class FifoQueueBase +{ +public: + + /// unique_ptr (with deleter) to storage + using StorageUniquePointer = std::unique_ptr<void, void(&)(void*)>; + + /** + * \brief FifoQueueBase's constructor + * + * \param [in] storageUniquePointer is a rvalue reference to StorageUniquePointer with storage for queue elements + * (sufficiently large for \a maxElements, each \a elementSize bytes long) and appropriate deleter + * \param [in] elementSize is the size of single queue element, bytes + * \param [in] maxElements is the number of elements in storage + */ + + FifoQueueBase(StorageUniquePointer&& storageUniquePointer, size_t elementSize, size_t maxElements); + + /** + * \brief FifoQueueBase's destructor + */ + + ~FifoQueueBase(); + + /** + * \return size of single queue element, bytes + */ + + size_t getElementSize() const + { + return elementSize_; + } + + /** + * \brief Implementation of pop() using type-erased functor + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a popSemaphore_ + * \param [in] functor is a reference to QueueFunctor which will execute actions related to popping - it will get + * readPosition_ as argument + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int pop(const SemaphoreFunctor& waitSemaphoreFunctor, const QueueFunctor& functor) + { + return popPush(waitSemaphoreFunctor, functor, popSemaphore_, pushSemaphore_, readPosition_); + } + + /** + * \brief Implementation of push() using type-erased functor + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] functor is a reference to QueueFunctor which will execute actions related to pushing - it will get + * writePosition_ as argument + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int push(const SemaphoreFunctor& waitSemaphoreFunctor, const QueueFunctor& functor) + { + return popPush(waitSemaphoreFunctor, functor, pushSemaphore_, popSemaphore_, writePosition_); + } + +private: + + /** + * \brief Implementation of pop() and push() using type-erased functor + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a waitSemaphore + * \param [in] functor is a reference to QueueFunctor which will execute actions related to popping/pushing - it + * will get \a storage as argument + * \param [in] waitSemaphore is a reference to semaphore that will be waited for, \a popSemaphore_ for pop(), \a + * pushSemaphore_ for push() + * \param [in] postSemaphore is a reference to semaphore that will be posted after the operation, \a pushSemaphore_ + * for pop(), \a popSemaphore_ for push() + * \param [in] storage is a reference to appropriate pointer to storage, which will be passed to \a functor, \a + * readPosition_ for pop(), \a writePosition_ for push() + * + * \return zero if operation was successful, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int popPush(const SemaphoreFunctor& waitSemaphoreFunctor, const QueueFunctor& functor, Semaphore& waitSemaphore, + Semaphore& postSemaphore, void*& storage); + + /// semaphore guarding access to "pop" functions - its value is equal to the number of available elements + Semaphore popSemaphore_; + + /// semaphore guarding access to "push" functions - its value is equal to the number of free slots + Semaphore pushSemaphore_; + + /// storage for queue elements + const StorageUniquePointer storageUniquePointer_; + + /// pointer to past-the-last element of storage for queue elements + const void* const storageEnd_; + + /// pointer to first element available for reading + void* readPosition_; + + /// pointer to first free slot available for writing + void* writePosition_; + + /// size of single queue element, bytes + const size_t elementSize_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_FIFOQUEUEBASE_HPP_ diff --git a/include/distortos/internal/synchronization/MemcpyPopQueueFunctor.hpp b/include/distortos/internal/synchronization/MemcpyPopQueueFunctor.hpp new file mode 100644 index 0000000..29113a5 --- /dev/null +++ b/include/distortos/internal/synchronization/MemcpyPopQueueFunctor.hpp @@ -0,0 +1,65 @@ +/** + * \file + * \brief MemcpyPopQueueFunctor class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MEMCPYPOPQUEUEFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MEMCPYPOPQUEUEFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/QueueFunctor.hpp" + +#include <cstddef> + +namespace distortos +{ + +namespace internal +{ + +/// MemcpyPopQueueFunctor is a functor used for popping of data from the raw queue with memecpy() +class MemcpyPopQueueFunctor : public QueueFunctor +{ +public: + + /** + * \brief MemcpyPopQueueFunctor's constructor + * + * \param [out] buffer is a pointer to buffer for popped element + * \param [in] size is the size of \a buffer, bytes + */ + + constexpr MemcpyPopQueueFunctor(void* const buffer, const size_t size) : + buffer_{buffer}, + size_{size} + { + + } + + /** + * \brief Copies the data from raw queue's storage (with memcpy()). + * + * \param [in,out] storage is a pointer to storage for element + */ + + void operator()(void* storage) const override; + +private: + + /// pointer to buffer for popped element + void* const buffer_; + + /// size of \a buffer_, bytes + const size_t size_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MEMCPYPOPQUEUEFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/MemcpyPushQueueFunctor.hpp b/include/distortos/internal/synchronization/MemcpyPushQueueFunctor.hpp new file mode 100644 index 0000000..89fb4b0 --- /dev/null +++ b/include/distortos/internal/synchronization/MemcpyPushQueueFunctor.hpp @@ -0,0 +1,65 @@ +/** + * \file + * \brief MemcpyPushQueueFunctor class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MEMCPYPUSHQUEUEFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MEMCPYPUSHQUEUEFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/QueueFunctor.hpp" + +#include <cstddef> + +namespace distortos +{ + +namespace internal +{ + +/// MemcpyPushQueueFunctor is a functor used for pushing of data to the raw queue with memcpy() +class MemcpyPushQueueFunctor : public QueueFunctor +{ +public: + + /** + * \brief MemcpyPushQueueFunctor's constructor + * + * \param [in] data is a pointer to data that will be pushed to raw queue + * \param [in] size is the size of \a data, bytes + */ + + constexpr MemcpyPushQueueFunctor(const void* const data, const size_t size) : + data_{data}, + size_{size} + { + + } + + /** + * \brief Copies the data to raw queue's storage (with memcpy()). + * + * \param [in,out] storage is a pointer to storage for element + */ + + void operator()(void* storage) const override; + +private: + + /// pointer to data that will be pushed to raw queue + const void* const data_; + + /// size of \a data_, bytes + const size_t size_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MEMCPYPUSHQUEUEFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/MessageQueueBase.hpp b/include/distortos/internal/synchronization/MessageQueueBase.hpp new file mode 100644 index 0000000..e0bd4e5 --- /dev/null +++ b/include/distortos/internal/synchronization/MessageQueueBase.hpp @@ -0,0 +1,221 @@ +/** + * \file + * \brief MessageQueueBase class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MESSAGEQUEUEBASE_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MESSAGEQUEUEBASE_HPP_ + +#include "distortos/Semaphore.hpp" + +#include "distortos/internal/synchronization/QueueFunctor.hpp" +#include "distortos/internal/synchronization/SemaphoreFunctor.hpp" + +#include "estd/SortedIntrusiveForwardList.hpp" + +#include <memory> + +namespace distortos +{ + +namespace internal +{ + +/// MessageQueueBase class implements basic functionality of MessageQueue template class +class MessageQueueBase +{ +public: + + /// entry in the MessageQueueBase + struct Entry + { + /** + * \brief Entry's constructor + * + * \param [in] priorityy is the priority of the entry + * \param [in] storagee is the storage for the entry + */ + + constexpr Entry(const uint8_t priorityy, void* const storagee) : + node{}, + priority{priorityy}, + storage{storagee} + { + + } + + /// node for intrusive forward list + estd::IntrusiveForwardListNode node; + + /// priority of the entry + uint8_t priority; + + /// storage for the entry + void* storage; + }; + + /// type of uninitialized storage for Entry + using EntryStorage = typename std::aligned_storage<sizeof(Entry), alignof(Entry)>::type; + + /// unique_ptr (with deleter) to EntryStorage[] + using EntryStorageUniquePointer = std::unique_ptr<EntryStorage[], void(&)(EntryStorage*)>; + + /** + * type of uninitialized storage for value + * + * \tparam T is the type of data in queue + */ + + template<typename T> + using ValueStorage = typename std::aligned_storage<sizeof(T), alignof(T)>::type; + + /// unique_ptr (with deleter) to storage + using ValueStorageUniquePointer = std::unique_ptr<void, void(&)(void*)>; + + /// functor which gives descending priority order of elements on the list + struct DescendingPriority + { + /** + * \brief DescendingPriority's constructor + */ + + constexpr DescendingPriority() + { + + } + + /** + * \brief DescendingPriority's function call operator + * + * \param [in] left is the object on the left side of comparison + * \param [in] right is the object on the right side of comparison + * + * \return true if left's priority is less than right's priority + */ + + bool operator()(const Entry& left, const Entry& right) const + { + return left.priority < right.priority; + } + }; + + /// type of entry list + using EntryList = estd::SortedIntrusiveForwardList<DescendingPriority, Entry, &Entry::node>; + + /// type of free entry list + using FreeEntryList = EntryList::UnsortedIntrusiveForwardList; + + /** + * \brief InternalFunctor is a type-erased interface for functors which execute common code of pop() and push() + * operations. + * + * The functor will be called by MessageQueueBase internals with references to \a entryList_ and \a freeEntryList_. + * It should perform common actions and execute the QueueFunctor passed from callers. + */ + + class InternalFunctor : public estd::TypeErasedFunctor<void(EntryList&, FreeEntryList&)> + { + + }; + + /** + * \brief MessageQueueBase's constructor + * + * \param [in] entryStorageUniquePointer is a rvalue reference to EntryStorageUniquePointer with storage for queue + * entries (sufficiently large for \a maxElements EntryStorage objects) and appropriate deleter + * \param [in] valueStorageUniquePointer is a rvalue reference to ValueStorageUniquePointer with storage for queue + * elements (sufficiently large for \a maxElements, each \a elementSize bytes long) and appropriate deleter + * \param [in] elementSize is the size of single queue element, bytes + * \param [in] maxElements is the number of elements in \a entryStorage array and valueStorage memory block + */ + + MessageQueueBase(EntryStorageUniquePointer&& entryStorageUniquePointer, + ValueStorageUniquePointer&& valueStorageUniquePointer, size_t elementSize, size_t maxElements); + + /** + * \brief MessageQueueBase's destructor + */ + + ~MessageQueueBase(); + + /** + * \brief Implementation of pop() using type-erased functor + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a popSemaphore_ + * \param [out] priority is a reference to variable that will be used to return priority of popped value + * \param [in] functor is a reference to QueueFunctor which will execute actions related to popping - it will get a + * pointer to storage with element + * + * \return zero if element was popped successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int pop(const SemaphoreFunctor& waitSemaphoreFunctor, uint8_t& priority, const QueueFunctor& functor); + + /** + * \brief Implementation of push() using type-erased functor + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a pushSemaphore_ + * \param [in] priority is the priority of new element + * \param [in] functor is a reference to QueueFunctor which will execute actions related to pushing - it will get a + * pointer to storage for element + * + * \return zero if element was pushed successfully, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int push(const SemaphoreFunctor& waitSemaphoreFunctor, uint8_t priority, const QueueFunctor& functor); + +private: + + /** + * \brief Implementation of pop() and push() using type-erased internal functor + * + * \param [in] waitSemaphoreFunctor is a reference to SemaphoreFunctor which will be executed with \a waitSemaphore + * \param [in] internalFunctor is a reference to InternalFunctor which will execute actions related to + * popping/pushing + * \param [in] waitSemaphore is a reference to semaphore that will be waited for, \a popSemaphore_ for pop(), \a + * pushSemaphore_ for push() + * \param [in] postSemaphore is a reference to semaphore that will be posted after the operation, \a pushSemaphore_ + * for pop(), \a popSemaphore_ for push() + * + * \return zero if operation was successful, error code otherwise: + * - error codes returned by \a waitSemaphoreFunctor's operator() call; + * - error codes returned by Semaphore::post(); + */ + + int popPush(const SemaphoreFunctor& waitSemaphoreFunctor, const InternalFunctor& internalFunctor, + Semaphore& waitSemaphore, Semaphore& postSemaphore); + + /// semaphore guarding access to "pop" functions - its value is equal to the number of available elements + Semaphore popSemaphore_; + + /// semaphore guarding access to "push" functions - its value is equal to the number of free slots + Semaphore pushSemaphore_; + + /// storage for queue entries + const EntryStorageUniquePointer entryStorageUniquePointer_; + + /// storage for queue elements + const ValueStorageUniquePointer valueStorageUniquePointer_; + + /// list of available entries, sorted in descending order of priority + EntryList entryList_; + + /// list of "free" entries + FreeEntryList freeEntryList_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MESSAGEQUEUEBASE_HPP_ diff --git a/include/distortos/internal/synchronization/MoveConstructQueueFunctor.hpp b/include/distortos/internal/synchronization/MoveConstructQueueFunctor.hpp new file mode 100644 index 0000000..ff6587f --- /dev/null +++ b/include/distortos/internal/synchronization/MoveConstructQueueFunctor.hpp @@ -0,0 +1,69 @@ +/** + * \file + * \brief MoveConstructQueueFunctor class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MOVECONSTRUCTQUEUEFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MOVECONSTRUCTQUEUEFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/QueueFunctor.hpp" + +#include <utility> + +namespace distortos +{ + +namespace internal +{ + +/** + * MoveConstructQueueFunctor is a functor used for pushing of data to the queue using move-construction + * + * \tparam T is the type of data pushed to the queue + */ + +template<typename T> +class MoveConstructQueueFunctor : public QueueFunctor +{ +public: + + /** + * \brief MoveConstructQueueFunctor's constructor + * + * \param [in] value is a rvalue reference to object that will be used as argument of move constructor + */ + + constexpr explicit MoveConstructQueueFunctor(T&& value) : + value_{std::move(value)} + { + + } + + /** + * \brief Move-constructs the element in the queue's storage + * + * \param [in,out] storage is a pointer to storage for element + */ + + void operator()(void* const storage) const override + { + new (storage) T{std::move(value_)}; + } + +private: + + /// rvalue reference to object that will be used as argument of move constructor + T&& value_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MOVECONSTRUCTQUEUEFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/MutexControlBlock.hpp b/include/distortos/internal/synchronization/MutexControlBlock.hpp new file mode 100644 index 0000000..93c3ee6 --- /dev/null +++ b/include/distortos/internal/synchronization/MutexControlBlock.hpp @@ -0,0 +1,184 @@ +/** + * \file + * \brief MutexControlBlock class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXCONTROLBLOCK_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXCONTROLBLOCK_HPP_ + +#include "distortos/internal/scheduler/ThreadList.hpp" + +#include "distortos/internal/synchronization/MutexListNode.hpp" + +#include "distortos/TickClock.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// MutexControlBlock class is a control block for Mutex +class MutexControlBlock : public MutexListNode +{ +public: + + /// mutex protocols + enum class Protocol : uint8_t + { + /// no protocol, similar to PTHREAD_PRIO_NONE + none, + /// priority inheritance protocol, similar to PTHREAD_PRIO_INHERIT + priorityInheritance, + /// priority protection protocol (Immediate Ceiling Priority Protocol), similar to PTHREAD_PRIO_PROTECT + priorityProtect, + }; + + /** + * \brief MutexControlBlock constructor + * + * \param [in] protocol is the mutex protocol + * \param [in] priorityCeiling is the priority ceiling of mutex, ignored when protocol != Protocol::priorityProtect + */ + + constexpr MutexControlBlock(const Protocol protocol, const uint8_t priorityCeiling) : + MutexListNode{}, + blockedList_{}, + owner_{}, + protocol_{protocol}, + priorityCeiling_{priorityCeiling} + { + + } + + /** + * \brief Blocks current thread, transferring it to blockedList_. + * + * \return 0 on success, error code otherwise: + * - values returned by Scheduler::block(); + */ + + int block(); + + /** + * \brief Blocks current thread with timeout, transferring it to blockedList_. + * + * \param [in] timePoint is the time point at which the thread will be unblocked (if not already unblocked) + * + * \return 0 on success, error code otherwise: + * - values returned by Scheduler::blockUntil(); + */ + + int blockUntil(TickClock::time_point timePoint); + + /** + * \brief Gets "boosted priority" of the mutex. + * + * "Boosted priority" of the mutex depends on the selected priority protocol: + * - None - 0, + * - PriorityInheritance - effective priority of the highest priority thread blocked on this mutex or 0 if no + * threads are blocked, + * - PriorityProtect - priority ceiling. + * + * \return "boosted priority" of the mutex + */ + + uint8_t getBoostedPriority() const; + + /** + * \return owner of the mutex, nullptr if mutex is currently unlocked + */ + + ThreadControlBlock* getOwner() const + { + return owner_; + } + + /** + * \return priority ceiling of mutex, valid only when protocol_ == Protocol::priorityProtect + */ + + uint8_t getPriorityCeiling() const + { + return priorityCeiling_; + } + + /** + * \return mutex protocol + */ + + Protocol getProtocol() const + { + return protocol_; + } + + /** + * \brief Performs actual locking of previously unlocked mutex. + * + * \attention mutex must be unlocked + */ + + void lock(); + + /** + * \brief Performs unlocking or transfer of lock from current owner to next thread on the list. + * + * Mutex is unlocked if blockedList_ is empty, otherwise the ownership is transfered to the next thread. + * + * \attention mutex must be locked + */ + + void unlockOrTransferLock(); + +private: + + /** + * \brief Performs action required for priority inheritance before actually blocking on the mutex. + * + * This must be called in block() and blockUntil() before actually blocking of the calling thread. + * + * \attantion mutex's protocol must be PriorityInheritance + */ + + void priorityInheritanceBeforeBlock() const; + + /** + * \brief Performs transfer of lock from current owner to next thread on the list. + * + * \attention mutex must be locked and blockedList_ must not be empty + */ + + void transferLock(); + + /** + * \brief Performs actual unlocking of previously locked mutex. + * + * \attention mutex must be locked and blockedList_ must be empty + */ + + void unlock(); + + /// ThreadControlBlock objects blocked on mutex + ThreadList blockedList_; + + /// owner of the mutex + ThreadControlBlock* owner_; + + /// mutex protocol + Protocol protocol_; + + /// priority ceiling of mutex, valid only when protocol_ == Protocol::priorityProtect + uint8_t priorityCeiling_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXCONTROLBLOCK_HPP_ diff --git a/include/distortos/internal/synchronization/MutexList.hpp b/include/distortos/internal/synchronization/MutexList.hpp new file mode 100644 index 0000000..18d5794 --- /dev/null +++ b/include/distortos/internal/synchronization/MutexList.hpp @@ -0,0 +1,32 @@ +/** + * \file + * \brief MutexList class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXLIST_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXLIST_HPP_ + +#include "distortos/internal/synchronization/MutexListNode.hpp" + +namespace distortos +{ + +namespace internal +{ + +class MutexControlBlock; + +/// intrusive list of mutexes (mutex control blocks) +using MutexList = estd::IntrusiveList<MutexListNode, &MutexListNode::node, MutexControlBlock>; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXLIST_HPP_ diff --git a/include/distortos/internal/synchronization/MutexListNode.hpp b/include/distortos/internal/synchronization/MutexListNode.hpp new file mode 100644 index 0000000..049eb76 --- /dev/null +++ b/include/distortos/internal/synchronization/MutexListNode.hpp @@ -0,0 +1,53 @@ +/** + * \file + * \brief MutexListNode class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXLISTNODE_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXLISTNODE_HPP_ + +#include "estd/IntrusiveList.hpp" + +namespace distortos +{ + +namespace internal +{ + +/** + * \brief MutexListNode class is a base for MutexControlBlock that serves as a node in intrusive list of mutexes (mutex + * control blocks) + * + * This class is needed to break circular dependency - MutexList is contained in ThreadControlBlock and ThreadList is + * contained in MutexControlBlock. + */ + +class MutexListNode +{ +public: + + /** + * \brief MutexListNode's constructor + */ + + constexpr MutexListNode() : + node{} + { + + } + + /// node for intrusive list + estd::IntrusiveListNode node; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXLISTNODE_HPP_ diff --git a/include/distortos/internal/synchronization/QueueFunctor.hpp b/include/distortos/internal/synchronization/QueueFunctor.hpp new file mode 100644 index 0000000..7c14a59 --- /dev/null +++ b/include/distortos/internal/synchronization/QueueFunctor.hpp @@ -0,0 +1,40 @@ +/** + * \file + * \brief QueueFunctor class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_QUEUEFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_QUEUEFUNCTOR_HPP_ + +#include "estd/TypeErasedFunctor.hpp" + +namespace distortos +{ + +namespace internal +{ + +/** + * \brief QueueFunctor is a type-erased interface for functors which execute some action on queue's storage (like + * copy-constructing, swapping, destroying, emplacing, ...). + * + * The functor will be called by queue internals with one argument - \a storage - which is a pointer to storage with/for + * element + */ + +class QueueFunctor : public estd::TypeErasedFunctor<void(void*)> +{ + +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_SYNCHRONIZATION_QUEUEFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/SemaphoreFunctor.hpp b/include/distortos/internal/synchronization/SemaphoreFunctor.hpp new file mode 100644 index 0000000..0b937ff --- /dev/null +++ b/include/distortos/internal/synchronization/SemaphoreFunctor.hpp @@ -0,0 +1,43 @@ +/** + * \file + * \brief SemaphoreFunctor class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHOREFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHOREFUNCTOR_HPP_ + +#include "estd/TypeErasedFunctor.hpp" + +namespace distortos +{ + +class Semaphore; + +namespace internal +{ + +/** + * \brief SemaphoreFunctor is a type-erased interface for functors which execute some action on semaphore (wait(), + * tryWait(), tryWaitFor(), tryWaitUntil(), ...). + * + * The functor will be called with one argument - \a semaphore - which is a reference to Semaphore object on which the + * action will be executed. Functor's operator should return zero if the action was executed successfully, error code + * otherwise. + */ + +class SemaphoreFunctor : public estd::TypeErasedFunctor<int(Semaphore&)> +{ + +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHOREFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/SemaphoreTryWaitForFunctor.hpp b/include/distortos/internal/synchronization/SemaphoreTryWaitForFunctor.hpp new file mode 100644 index 0000000..88580de --- /dev/null +++ b/include/distortos/internal/synchronization/SemaphoreTryWaitForFunctor.hpp @@ -0,0 +1,62 @@ +/** + * \file + * \brief SemaphoreTryWaitForFunctor class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITFORFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITFORFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/SemaphoreFunctor.hpp" + +#include "distortos/TickClock.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// SemaphoreTryWaitForFunctor class is a SemaphoreFunctor which calls Semaphore::tryWaitFor() with bounded duration +class SemaphoreTryWaitForFunctor : public SemaphoreFunctor +{ +public: + + /** + * \brief SemaphoreTryWaitForFunctor's constructor + * + * \param [in] duration is the bounded duration for Semaphore::tryWaitFor() call + */ + + constexpr explicit SemaphoreTryWaitForFunctor(const TickClock::duration duration) : + duration_{duration} + { + + } + + /** + * \brief Calls Semaphore::tryWaitFor() with bounded duration + * + * \param [in] semaphore is a reference to Semaphore object for which Semaphore::tryWaitFor() will be called + * + * \return value returned by Semaphore::tryWaitFor() + */ + + int operator()(Semaphore& semaphore) const override; + +private: + + /// bounded duration for Semaphore::tryWaitFor() call + const TickClock::duration duration_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITFORFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/SemaphoreTryWaitFunctor.hpp b/include/distortos/internal/synchronization/SemaphoreTryWaitFunctor.hpp new file mode 100644 index 0000000..4084563 --- /dev/null +++ b/include/distortos/internal/synchronization/SemaphoreTryWaitFunctor.hpp @@ -0,0 +1,43 @@ +/** + * \file + * \brief SemaphoreTryWaitFunctor class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/SemaphoreFunctor.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// SemaphoreTryWaitFunctor class is a SemaphoreFunctor which calls Semaphore::tryWait() +class SemaphoreTryWaitFunctor : public SemaphoreFunctor +{ +public: + + /** + * \brief Calls Semaphore::tryWait() + * + * \param [in] semaphore is a reference to Semaphore object for which Semaphore::tryWait() will be called + * + * \return value returned by Semaphore::tryWait() + */ + + int operator()(Semaphore& semaphore) const override; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/SemaphoreTryWaitUntilFunctor.hpp b/include/distortos/internal/synchronization/SemaphoreTryWaitUntilFunctor.hpp new file mode 100644 index 0000000..a8eaab2 --- /dev/null +++ b/include/distortos/internal/synchronization/SemaphoreTryWaitUntilFunctor.hpp @@ -0,0 +1,63 @@ +/** + * \file + * \brief SemaphoreTryWaitUntilFunctor class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITUNTILFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITUNTILFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/SemaphoreFunctor.hpp" + +#include "distortos/TickClock.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// SemaphoreTryWaitUntilFunctor class is a SemaphoreFunctor which calls Semaphore::tryWaitUntil() with bounded time +/// point +class SemaphoreTryWaitUntilFunctor : public SemaphoreFunctor +{ +public: + + /** + * \brief SemaphoreTryWaitUntilFunctor's constructor + * + * \param [in] timePoint is the bounded time point for Semaphore::tryWaitUntil() call + */ + + constexpr explicit SemaphoreTryWaitUntilFunctor(const TickClock::time_point timePoint) : + timePoint_{timePoint} + { + + } + + /** + * \brief Calls Semaphore::tryWaitUntil() with bounded time point. + * + * \param [in] semaphore is a reference to Semaphore object for which Semaphore::tryWaitUntil() will be called + * + * \return value returned by Semaphore::tryWaitUntil() + */ + + int operator()(Semaphore& semaphore) const override; + +private: + + /// bounded time point for Semaphore::tryWaitUntil() call + const TickClock::time_point timePoint_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHORETRYWAITUNTILFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/SemaphoreWaitFunctor.hpp b/include/distortos/internal/synchronization/SemaphoreWaitFunctor.hpp new file mode 100644 index 0000000..a787b2b --- /dev/null +++ b/include/distortos/internal/synchronization/SemaphoreWaitFunctor.hpp @@ -0,0 +1,43 @@ +/** + * \file + * \brief SemaphoreWaitFunctor class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHOREWAITFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHOREWAITFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/SemaphoreFunctor.hpp" + +namespace distortos +{ + +namespace internal +{ + +/// SemaphoreWaitFunctor class is a SemaphoreFunctor which calls Semaphore::wait() +class SemaphoreWaitFunctor : public SemaphoreFunctor +{ +public: + + /** + * \brief Calls Semaphore::wait() + * + * \param [in] semaphore is a reference to Semaphore object for which Semaphore::wait() will be called + * + * \return value returned by Semaphore::wait() + */ + + int operator()(Semaphore& semaphore) const override; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SEMAPHOREWAITFUNCTOR_HPP_ diff --git a/include/distortos/internal/synchronization/SignalInformationQueue.hpp b/include/distortos/internal/synchronization/SignalInformationQueue.hpp new file mode 100644 index 0000000..7a9d08a --- /dev/null +++ b/include/distortos/internal/synchronization/SignalInformationQueue.hpp @@ -0,0 +1,123 @@ +/** + * \file + * \brief SignalInformationQueue class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALINFORMATIONQUEUE_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALINFORMATIONQUEUE_HPP_ + +#include "distortos/SignalInformation.hpp" + +#include "estd/IntrusiveForwardList.hpp" + +#include <memory> + +namespace distortos +{ + +class SignalSet; + +namespace internal +{ + +/// SignalInformationQueue class can be used for queuing of SignalInformation objects +class SignalInformationQueue +{ +public: + + /// single node of internal forward list - estd::IntrusiveForwardListNode and SignalInformation + struct QueueNode + { + /// node for intrusive forward list + estd::IntrusiveForwardListNode node; + + /// queued SignalInformation + SignalInformation signalInformation; + }; + + /// type of uninitialized storage for QueueNode + using Storage = typename std::aligned_storage<sizeof(QueueNode), alignof(QueueNode)>::type; + + /// unique_ptr (with deleter) to Storage[] + using StorageUniquePointer = std::unique_ptr<Storage[], void(&)(Storage*)>; + + /** + * \brief SignalInformationQueue's constructor + * + * \param [in] storageUniquePointer is a rvalue reference to StorageUniquePointer with storage for queue elements + * (sufficiently large for \a maxElements Storage objects) and appropriate deleter + * \param [in] maxElements is the number of elements in \a storage array + */ + + SignalInformationQueue(StorageUniquePointer&& storageUniquePointer, size_t maxElements); + + /** + * \brief SignalInformationQueue's destructor + */ + + ~SignalInformationQueue(); + + /** + * \brief Accepts (dequeues) one of signals that are queued. + * + * This should be called when the signal is "accepted". + * + * \param [in] signalNumber is the signal that will be accepted, [0; 31] + * + * \return pair with return code (0 on success, error code otherwise) and dequeued SignalInformation object; + * error codes: + * - EAGAIN - no SignalInformation object with signal number equal to \a signalNumber was queued; + */ + + std::pair<int, SignalInformation> acceptQueuedSignal(uint8_t signalNumber); + + /** + * \return set of currently queued signals + */ + + SignalSet getQueuedSignalSet() const; + + /** + * \brief Adds the signalNumber and signal value (sigval union) to list of queued SignalInformation objects. + * + * \param [in] signalNumber is the signal that will be queued, [0; 31] + * \param [in] value is the signal value + * + * \return 0 on success, error code otherwise: + * - EAGAIN - no resources are available to queue the signal, \a maxElements signals are already queued; + * - EINVAL - \a signalNumber value is invalid; + */ + + int queueSignal(uint8_t signalNumber, sigval value); + + SignalInformationQueue(const SignalInformationQueue&) = delete; + SignalInformationQueue(SignalInformationQueue&&) = default; + const SignalInformationQueue& operator=(const SignalInformationQueue&) = delete; + SignalInformationQueue& operator=(SignalInformationQueue&&) = delete; + +private: + + /// type of container with SignalInformation objects + using List = estd::IntrusiveForwardList<QueueNode, &QueueNode::node>; + + /// storage for queue elements + StorageUniquePointer storageUniquePointer_; + + /// list of queued SignalInformation objects + List signalInformationList_; + + /// list of "free" SignalInformation objects + List freeSignalInformationList_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALINFORMATIONQUEUE_HPP_ diff --git a/include/distortos/internal/synchronization/SignalsCatcherControlBlock.hpp b/include/distortos/internal/synchronization/SignalsCatcherControlBlock.hpp new file mode 100644 index 0000000..6676216 --- /dev/null +++ b/include/distortos/internal/synchronization/SignalsCatcherControlBlock.hpp @@ -0,0 +1,213 @@ +/** + * \file + * \brief SignalsCatcherControlBlock class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALSCATCHERCONTROLBLOCK_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALSCATCHERCONTROLBLOCK_HPP_ + +#include "distortos/SignalAction.hpp" + +#include <memory> + +namespace distortos +{ + +namespace internal +{ + +class SignalsReceiverControlBlock; +class ThreadControlBlock; + +/// SignalsCatcherControlBlock class is a structure required by threads for "catching" and "handling" of signals +class SignalsCatcherControlBlock +{ +public: + + /// association of signal numbers (as SignalSet) with SignalAction + using Association = std::pair<SignalSet, SignalAction>; + + /// type of uninitialized storage for Association objects + using Storage = std::aligned_storage<sizeof(Association), alignof(Association)>::type; + + /// unique_ptr (with deleter) to Storage[] + using StorageUniquePointer = std::unique_ptr<Storage[], void(&)(Storage*)>; + + /** + * \brief SignalsCatcherControlBlock's constructor + * + * \param [in] storageUniquePointer is a rvalue reference to StorageUniquePointer with storage for Association + * objects (sufficiently large for \a storageSize elements) and appropriate deleter + * \param [in] storageSize is the number of elements in \a storage array + */ + + SignalsCatcherControlBlock(StorageUniquePointer&& storageUniquePointer, size_t storageSize); + + /** + * \brief SignalsCatcherControlBlock's destructor + */ + + ~SignalsCatcherControlBlock(); + + /** + * \brief Hook function executed when delivery of signals is started. + * + * Clears "delivery pending" flag. + * + * \attention This function should be called only by SignalsReceiverControlBlock::deliveryOfSignalsFinishedHook(). + */ + + void deliveryOfSignalsStartedHook() + { + deliveryIsPending_ = false; + } + + /** + * \brief Gets SignalAction associated with given signal number. + * + * \param [in] signalNumber is the signal for which the association is requested, [0; 31] + * + * \return pair with return code (0 on success, error code otherwise) and SignalAction that is associated with + * \a signalNumber, default-constructed object if no association was found; + * error codes: + * - EINVAL - \a signalNumber value is invalid; + */ + + std::pair<int, SignalAction> getAssociation(uint8_t signalNumber) const; + + /** + * \return SignalSet with signal mask for associated thread + */ + + SignalSet getSignalMask() const + { + return signalMask_; + } + + /** + * \brief Part of SignalsReceiverControlBlock::postGenerate() specific to catching unmasked signals. + * + * Requests delivery of signals to associated thread if there is some non-default signal handler for the signal. + * + * \param [in] signalNumber is the unmasked signal that was generated, [0; 31] + * \param [in] threadControlBlock is a reference to associated ThreadControlBlock + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + */ + + int postGenerate(uint8_t signalNumber, ThreadControlBlock& threadControlBlock); + + /** + * \brief Sets association for given signal number. + * + * \param [in] signalNumber is the signal for which the association will be set, [0; 31] + * \param [in] signalAction is a reference to SignalAction that will be associated with given signal number, object + * in internal storage is copy-constructed + * + * \return pair with return code (0 on success, error code otherwise) and SignalAction that was associated with + * \a signalNumber, default-constructed object if no association was found; + * error codes: + * - EAGAIN - no resources are available to associate \a signalNumber with \a signalAction; + * - EINVAL - \a signalNumber value is invalid; + */ + + std::pair<int, SignalAction> setAssociation(uint8_t signalNumber, const SignalAction& signalAction); + + /** + * \brief Sets signal mask for associated thread. + * + * If any pending signal is unblocked and \a owner doesn't equal nullptr, then delivery of signals to associated + * thread will be requested. + * + * \param [in] signalMask is the SignalSet with new signal mask for associated thread + * \param [in] owner selects whether delivery of signals will be requested if any pending signal is unblocked + * (pointer to owner SignalsReceiverControlBlock object) or not (nullptr) + */ + + void setSignalMask(SignalSet signalMask, const SignalsReceiverControlBlock* owner); + + SignalsCatcherControlBlock(const SignalsCatcherControlBlock&) = delete; + SignalsCatcherControlBlock(SignalsCatcherControlBlock&&) = default; + const SignalsCatcherControlBlock& operator=(const SignalsCatcherControlBlock&) = delete; + SignalsCatcherControlBlock& operator=(SignalsCatcherControlBlock&&) = delete; + +private: + + /** + * \brief Clears association for given signal number. + * + * \param [in] signalNumber is the signal for which the association will be cleared, [0; 31] + * + * \return SignalAction that was associated with \a signalNumber, default-constructed object if no association was + * found + */ + + SignalAction clearAssociation(uint8_t signalNumber); + + /** + * \brief Clears given association for given signal number. + * + * \param [in] signalNumber is the signal for which the association will be cleared, [0; 31] + * \param [in] association is a reference to Association object from <em>[associationsBegin_; associationsEnd_)</em> + * range that will be removed + * + * \return SignalAction from \a association + */ + + SignalAction clearAssociation(uint8_t signalNumber, Association& association); + + /** + * \return pointer to first element of range of Association objects + */ + + Association* getAssociationsBegin() const + { + return reinterpret_cast<Association*>(storageUniquePointer_.get()); + } + + /** + * \brief Requests delivery of signals to associated thread. + * + * Delivery of signals (via special function executed in the associated thread) is requested only if it's not + * already pending. The thread is unblocked if it was blocked. + * + * \param [in] threadControlBlock is a reference to associated ThreadControlBlock + */ + + void requestDeliveryOfSignals(ThreadControlBlock& threadControlBlock); + + /// storage for Association objects + StorageUniquePointer storageUniquePointer_; + + /// SignalSet with signal mask for associated thread + SignalSet signalMask_; + + /// union binds \a associationsEnd_ and \a storageBegin_ - these point to the same address + union + { + /// pointer to "one past the last" element of range of Association objects + Association* associationsEnd_; + + /// pointer to first element of range of Storage objects + Storage* storageBegin_; + }; + + /// pointer to "one past the last" element of range of Storage objects + Storage* storageEnd_; + + /// true if signal delivery is pending, false otherwise + bool deliveryIsPending_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALSCATCHERCONTROLBLOCK_HPP_ diff --git a/include/distortos/internal/synchronization/SignalsReceiverControlBlock.hpp b/include/distortos/internal/synchronization/SignalsReceiverControlBlock.hpp new file mode 100644 index 0000000..c70d79d --- /dev/null +++ b/include/distortos/internal/synchronization/SignalsReceiverControlBlock.hpp @@ -0,0 +1,243 @@ +/** + * \file + * \brief SignalsReceiverControlBlock class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALSRECEIVERCONTROLBLOCK_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALSRECEIVERCONTROLBLOCK_HPP_ + +#include "distortos/SignalSet.hpp" + +#include <cstdint> + +union sigval; + +namespace distortos +{ + +class SignalAction; +class SignalInformation; +class SignalInformationQueueWrapper; +class SignalsCatcher; + +namespace internal +{ + +class SignalInformationQueue; +class SignalsCatcherControlBlock; +class ThreadControlBlock; + +/// SignalsReceiverControlBlock class is a structure required by threads for "receiving" of signals +class SignalsReceiverControlBlock +{ +public: + + /** + * \brief SignalsReceiverControlBlock's constructor + * + * \param [in] signalInformationQueueWrapper is a pointer to SignalInformationQueueWrapper for this receiver, + * nullptr to disable queuing of signals for this receiver + * \param [in] signalsCatcher is a pointer to SignalsCatcher for this receiver, nullptr if this receiver cannot + * catch/handle signals + */ + + explicit SignalsReceiverControlBlock(SignalInformationQueueWrapper* signalInformationQueueWrapper, + SignalsCatcher* signalsCatcher); + + /** + * \brief Accepts (clears) one of signals that are pending. + * + * This should be called when the signal is "accepted". + * + * \param [in] signalNumber is the signal that will be accepted, [0; 31] + * + * \return pair with return code (0 on success, error code otherwise) and SignalInformation object for accepted + * signal; error codes: + * - EAGAIN - no signal specified by \a signalNumber was pending; + * - EINVAL - \a signalNumber value is invalid; + */ + + std::pair<int, SignalInformation> acceptPendingSignal(uint8_t signalNumber); + + /** + * \brief Hook function executed when delivery of signals is started. + * + * Calls SignalsCatcherControlBlock::deliveryOfSignalsStartedHook(). + * + * \attention This function should be called only by the function that delivers signals (<em>deliverSignals()</em>). + * + * \return 0 on success, error code otherwise: + * - ENOTSUP - catching/handling of signals is disabled for this receiver; + */ + + int deliveryOfSignalsStartedHook() const; + + /** + * \brief Generates signal for associated thread. + * + * Similar to pthread_kill() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html + * + * Adds the signalNumber to set of pending signals. If associated thread is currently waiting for this signal, it + * will be unblocked. + * + * \param [in] signalNumber is the signal that will be generated, [0; 31] + * \param [in] threadControlBlock is a reference to associated ThreadControlBlock + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + */ + + int generateSignal(uint8_t signalNumber, ThreadControlBlock& threadControlBlock); + + /** + * \return set of currently pending signals + */ + + SignalSet getPendingSignalSet() const; + + /** + * \brief Gets SignalAction associated with given signal number. + * + * Similar to sigaction() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html + * + * \param [in] signalNumber is the signal for which the association is requested, [0; 31] + * + * \return pair with return code (0 on success, error code otherwise) and SignalAction that is associated with + * \a signalNumber, default-constructed object if no association was found; + * error codes: + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - catching/handling of signals is disabled for this receiver; + */ + + std::pair<int, SignalAction> getSignalAction(uint8_t signalNumber) const; + + /** + * \brief Gets signal mask for associated thread. + * + * Similar to pthread_sigmask() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html# + * + * \return SignalSet with signal mask for associated thread + */ + + SignalSet getSignalMask() const; + + /** + * \brief Queues signal for associated thread. + * + * Similar to sigqueue() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigqueue.html + * + * Queues the signalNumber and signal value (sigval union) in associated SignalInformationQueue object. If + * associated thread is currently waiting for this signal, it will be unblocked. + * + * \param [in] signalNumber is the signal that will be queued, [0; 31] + * \param [in] value is the signal value + * \param [in] threadControlBlock is a reference to associated ThreadControlBlock + * + * \return 0 on success, error code otherwise: + * - EAGAIN - no resources are available to queue the signal, maximal number of signals is already queued in + * associated SignalInformationQueue object; + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - queuing of signals is disabled for this receiver; + */ + + int queueSignal(uint8_t signalNumber, sigval value, ThreadControlBlock& threadControlBlock) const; + + /** + * \brief Sets association for given signal number. + * + * Similar to sigaction() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html + * + * \param [in] signalNumber is the signal for which the association will be set, [0; 31] + * \param [in] signalAction is a reference to SignalAction that will be associated with given signal number, object + * in internal storage is copy-constructed + * + * \return pair with return code (0 on success, error code otherwise) and SignalAction that was associated with + * \a signalNumber, default-constructed object if no association was found; + * error codes: + * - EAGAIN - no resources are available to associate \a signalNumber with \a signalAction; + * - EINVAL - \a signalNumber value is invalid; + * - ENOTSUP - catching/handling of signals is disabled for this receiver; + */ + + std::pair<int, SignalAction> setSignalAction(uint8_t signalNumber, const SignalAction& signalAction); + + /** + * \brief Sets signal mask for associated thread. + * + * Similar to pthread_sigmask() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html# + * + * \param [in] signalMask is the SignalSet with new signal mask for associated thread + * \param [in] requestDelivery selects whether delivery of signals will be requested if any pending signal is + * unblocked (true) or not (false) + * + * \return 0 on success, error code otherwise: + * - ENOTSUP - catching/handling of signals is disabled for this receiver; + */ + + int setSignalMask(SignalSet signalMask, bool requestDelivery); + + /** + * \param [in] signalSet is a pointer to set of signals that will be "waited for", nullptr when wait was terminated + */ + + void setWaitingSignalSet(const SignalSet* const signalSet) + { + waitingSignalSet_ = signalSet; + } + +private: + + /** + * \brief Checks whether signal is ignored. + * + * Signal is ignored if it has no SignalAction object associated. Signal is never ignored if catching/handling of + * signals is disabled for this receiver. + * + * \param [in] signalNumber is the signal for which the check will be performed, [0; 31] + * + * \return pair with return code (0 on success, error code otherwise) and boolean telling whether the signal is + * ignored (true) or not (false); + * error codes: + * - EINVAL - \a signalNumber value is invalid; + */ + + std::pair<int, bool> isSignalIgnored(uint8_t signalNumber) const; + + /** + * \brief Actions executed after signal is "generated" with generateSignal() or queueSignal(). + * + * If associated thread is currently waiting for the signal that was generated, it will be unblocked. + * + * \param [in] signalNumber is the signal that was generated, [0; 31] + * \param [in] threadControlBlock is a reference to associated ThreadControlBlock + * + * \return 0 on success, error code otherwise: + * - EINVAL - \a signalNumber value is invalid; + */ + + int postGenerate(uint8_t signalNumber, ThreadControlBlock& threadControlBlock) const; + + /// set of pending signals + SignalSet pendingSignalSet_; + + /// pointer to set of "waited for" signals, nullptr if associated thread is not waiting for any signals + const SignalSet* waitingSignalSet_; + + /// pointer to SignalsCatcherControlBlock for this receiver, nullptr if this receiver cannot catch/handle signals + SignalsCatcherControlBlock* signalsCatcherControlBlock_; + + /// pointer to SignalInformationQueue for this receiver, nullptr if this receiver cannot queue signals + SignalInformationQueue* signalInformationQueue_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SIGNALSRECEIVERCONTROLBLOCK_HPP_ diff --git a/include/distortos/internal/synchronization/SwapPopQueueFunctor.hpp b/include/distortos/internal/synchronization/SwapPopQueueFunctor.hpp new file mode 100644 index 0000000..1009f3c --- /dev/null +++ b/include/distortos/internal/synchronization/SwapPopQueueFunctor.hpp @@ -0,0 +1,74 @@ +/** + * \file + * \brief SwapPopQueueFunctor class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SWAPPOPQUEUEFUNCTOR_HPP_ +#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SWAPPOPQUEUEFUNCTOR_HPP_ + +#include "distortos/internal/synchronization/QueueFunctor.hpp" + +#include <utility> + +namespace distortos +{ + +namespace internal +{ + +/** + * SwapPopQueueFunctor is a functor used for popping of data from the queue using swap + * + * \tparam T is the type of data popped from the queue + */ + +template<typename T> +class SwapPopQueueFunctor : public QueueFunctor +{ +public: + + /** + * \brief SwapPopQueueFunctor's constructor + * + * \param [out] value is a reference to object that will be used to return popped value, its contents are swapped + * with the value in the queue's storage and destructed when no longer needed + */ + + constexpr explicit SwapPopQueueFunctor(T& value) : + value_{value} + { + + } + + /** + * \brief Swaps the element in the queue's storage with the value provided by user and destroys this value when no + * longer needed. + * + * \param [in,out] storage is a pointer to storage with element + */ + + void operator()(void* const storage) const override + { + auto& swappedValue = *reinterpret_cast<T*>(storage); + using std::swap; + swap(value_, swappedValue); + swappedValue.~T(); + } + +private: + + /// reference to object that will be used to return popped value + T& value_; +}; + +} // namespace internal + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_SWAPPOPQUEUEFUNCTOR_HPP_ diff --git a/include/distortos/statistics.hpp b/include/distortos/statistics.hpp new file mode 100644 index 0000000..68645ef --- /dev/null +++ b/include/distortos/statistics.hpp @@ -0,0 +1,38 @@ +/** + * \file + * \brief statistics namespace header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_DISTORTOS_STATISTICS_HPP_ +#define INCLUDE_DISTORTOS_STATISTICS_HPP_ + +#include <cstdint> + +namespace distortos +{ + +namespace statistics +{ + +/// \addtogroup statistics +/// \{ + +/** + * \return number of context switches + */ + +uint64_t getContextSwitchCount(); + +/// \} + +} // namespace statistics + +} // namespace distortos + +#endif // INCLUDE_DISTORTOS_STATISTICS_HPP_ diff --git a/include/estd/ContiguousRange.hpp b/include/estd/ContiguousRange.hpp new file mode 100644 index 0000000..adfc8a0 --- /dev/null +++ b/include/estd/ContiguousRange.hpp @@ -0,0 +1,164 @@ +/** + * \file + * \brief ContiguousRange template class header. + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_CONTIGUOUSRANGE_HPP_ +#define ESTD_CONTIGUOUSRANGE_HPP_ + +#include <iterator> + +namespace estd +{ + +/** + * \brief ContiguousRange template class is a pair of iterators to contiguous sequence of elements in memory + * + * \tparam T is the type of data in the range + */ + +template<typename T> +class ContiguousRange +{ +public: + + /// value_type type + using value_type = T; + + /// pointer type + using pointer = value_type*; + + /// const_pointer type + using const_pointer = const value_type*; + + /// reference type + using reference = value_type&; + + /// const_reference type + using const_reference = const value_type&; + + /// iterator type + using iterator = value_type*; + + /// const_iterator type + using const_iterator = const value_type*; + + /// size_type type + using size_type = std::size_t; + + /// difference_type type + using difference_type = std::ptrdiff_t; + + /// reverse_iterator type + using reverse_iterator = std::reverse_iterator<iterator>; + + /// const_reverse_iterator type + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + /** + * \brief ContiguousRange's constructor. + * + * \param [in] beginn is an iterator to first element in the range + * \param [in] endd is an iterator to "one past the last" element in the range + */ + + constexpr ContiguousRange(const iterator beginn, const iterator endd) noexcept : + begin_{beginn}, + end_{endd} + { + + } + + /** + * \brief Empty ContiguousRange's constructor. + */ + + constexpr explicit ContiguousRange() noexcept : + ContiguousRange{nullptr, nullptr} + { + + } + + /** + * \brief ContiguousRange's constructor using C-style array. + * + * \tparam N is the number of elements in the array + * + * \param [in] array is the array used to initialize the range + */ + + template<size_t N> + constexpr explicit ContiguousRange(T (& array)[N]) noexcept : + ContiguousRange{array, array + N} + { + + } + + /** + * \brief ContiguousRange's constructor using single value + * + * \param [in] value is a reference to variable used to initialize the range + */ + + constexpr explicit ContiguousRange(T& value) noexcept : + ContiguousRange{&value, &value + 1} + { + + } + + /** + * \return iterator to first element in the range + */ + + constexpr iterator begin() const noexcept + { + return begin_; + } + + /** + * \return iterator to "one past the last" element in the range + */ + + constexpr iterator end() const noexcept + { + return end_; + } + + /** + * \return number of elements in the range + */ + + constexpr size_type size() const noexcept + { + return end_ - begin_; + } + + /** + * \param [in] i is the index of element that will be accessed + * + * \return reference to element at given index + */ + + reference operator[](const size_type i) const noexcept + { + return begin_[i]; + } + +private: + + /// iterator to first element in the range + iterator begin_; + + /// iterator to "one past the last" element in the range + iterator end_; +}; + +} // namespace estd + +#endif // ESTD_CONTIGUOUSRANGE_HPP_ diff --git a/include/estd/IntegerSequence.hpp b/include/estd/IntegerSequence.hpp new file mode 100644 index 0000000..a0776f5 --- /dev/null +++ b/include/estd/IntegerSequence.hpp @@ -0,0 +1,229 @@ +/** + * \file + * \brief IntegerSequence template class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_INTEGERSEQUENCE_HPP_ +#define ESTD_INTEGERSEQUENCE_HPP_ + +#include <type_traits> + +namespace estd +{ + +/** + * \brief Compile-time sequence of integers + * + * Similar to std::integer_sequence from C++14 - http://en.cppreference.com/w/cpp/utility/integer_sequence + * + * \tparam T is an integer type to use for the elements of the sequence + * \tparam Integers is a non-type parameter pack representing the sequence + */ + +template<typename T, T... Integers> +class IntegerSequence +{ +public: + + /// integer type used for the elements of the sequence + using value_type = T; + + /** + * \return number of elements in the sequence + */ + + constexpr static std::size_t size() noexcept + { + return sizeof...(Integers); + }; +}; + +/** + * \brief Compile-time sequence of std::size_t elements + * + * Similar to std::index_sequence from C++14 - http://en.cppreference.com/w/cpp/utility/integer_sequence + * + * \tparam Indexes is a non-type parameter pack representing the sequence + */ + +template<std::size_t... Indexes> +using IndexSequence = IntegerSequence<std::size_t, Indexes...>; + +namespace internal +{ + +/** + * \brief IntegerSequence with two internal type aliases. + * + * \tparam T is an integer type to use for the elements of the sequence + * \tparam Integers is a non-type parameter pack representing the sequence + */ + +template<typename T, T... Integers> +struct TypedSequence : IntegerSequence<T, Integers...> +{ + /// type of base class + using base = IntegerSequence<T, Integers...>; + + /// type of class + using type = TypedSequence; +}; + +/** + * \brief TypedSequence with doubled number of elements + * + * \tparam Sequence is the type of sequence that will be doubled + */ + +template<typename Sequence> +struct DoubledIntegerSequence; + +/** + * \brief TypedSequence with doubled number of elements + * + * Specialization for TypedSequence. + * + * \tparam T is an integer type to use for the elements of the sequence + * \tparam Integers is a non-type parameter pack representing the sequence + */ + +template<typename T, T... Integers> +struct DoubledIntegerSequence<TypedSequence<T, Integers...>> +{ + /// TypedSequence with doubled number of elements - TypedSequence<T, 0, 1, ..., N - 1> is turned into + /// TypedSequence<T, 0, 1, ..., N - 1, N, N + 1, ..., 2 * N - 1> + using type = TypedSequence<T, Integers..., (sizeof...(Integers) + Integers)...>; +}; + +/** + * \brief TypedSequence optionally extended by one element + * + * \tparam Extend selects whether the sequence will be extended by one element (true) or not (false) + * \tparam Sequence is the type of sequence that will optionally be extended + */ + +template<bool Extend, typename Sequence> +struct ExtendedIntegerSequence +{ + /// same as \a Sequence + using type = Sequence; +}; + +/** + * \brief TypedSequence optionally extended by one element + * + * Specialization for the case with extending. + * + * \tparam T is an integer type to use for the elements of the sequence + * \tparam Integers is a non-type parameter pack representing the sequence + */ + +template<typename T, T... Integers> +struct ExtendedIntegerSequence<true, TypedSequence<T, Integers...>> +{ + /// sequence extended by one element - TypedSequence<T, 0, 1, ..., N - 1> is turned into + /// TypedSequence<T, 0, 1, ..., N - 1, N> + using type = TypedSequence<T, Integers..., sizeof...(Integers)>; +}; + +/** + * \brief Implementation of generator of IntegerSequence types + * + * Generates TypedSequence<T, 0, 1, ..., N - 1> type. + * + * \tparam T is an integer type to use for the elements of the sequence + * \tparam N is the requested number of elements in the sequence + */ + +template<typename T, std::size_t N> +struct MakeIntegerSequenceImplementation : + ExtendedIntegerSequence<N % 2 != 0, + typename DoubledIntegerSequence<typename MakeIntegerSequenceImplementation<T, N / 2>::type>::type> +{ + +}; + +/** + * \brief Implementation of generator of IntegerSequence types + * + * Specialization for terminal case - 0 elements - generates TypedSequence<T> type. + * + * \tparam T is an integer type to use for the elements of the sequence + */ + +template<typename T> +struct MakeIntegerSequenceImplementation<T, 0> +{ + /// empty TypedSequence<T> type + using type = TypedSequence<T>; +}; + +/** + * \brief Wrapper for MakeIntegerSequenceImplementation that ensures \a N is non-negative + * + * Generates TypedSequence<T, 0, 1, ..., N - 1> type. + * + * \tparam T is an integer type to use for the elements of the sequence + * \tparam N is the requested number of elements in the sequence, must be non-negative + */ + +template<typename T, T N> +struct MakeIntegerSequenceImplementationWrapper : + std::enable_if<N >= 0, MakeIntegerSequenceImplementation<T, static_cast<std::size_t>(N)>>::type +{ + static_assert(N >= 0, "Number of elements in the sequence must be non-negative!"); +}; + +} // namespace internal + +/** + * \brief Generator of IntegerSequence types + * + * Similar to std::make_integer_sequence from C++14 - http://en.cppreference.com/w/cpp/utility/integer_sequence + * + * Whole implementation is based on code from http://stackoverflow.com/a/20101039/157344 + * + * Generates IntegerSequence<T, 0, 1, ..., N - 1> type. + * + * \tparam T is an integer type to use for the elements of the sequence + * \tparam N is the requested number of elements in the sequence + */ + +template<typename T, T N> +using MakeIntegerSequence = typename internal::MakeIntegerSequenceImplementationWrapper<T, N>::type::base; + +/** + * \brief Generator of IndexSequence types + * + * Similar to std::make_index_sequence from C++14 - http://en.cppreference.com/w/cpp/utility/integer_sequence + * + * Generates IndexSequence<0, 1, ..., N - 1> type. + * + * \tparam N is the requested number of elements in the sequence + */ + +template<std::size_t N> +using MakeIndexSequence = MakeIntegerSequence<std::size_t, N>; + +/** + * \brief Generator of IndexSequence types + * + * Similar to std::index_sequence_for from C++14 - http://en.cppreference.com/w/cpp/utility/integer_sequence + * + * Generates IndexSequence<0, 1, ..., sizeof...(T) - 1> type. + * + * \tparam T is the type parameter pack for which an index sequence of the same length will be generated + */ + +template<typename... T> +using IndexSequenceFor = MakeIndexSequence<sizeof...(T)>; + +} // namespace estd + +#endif // ESTD_INTEGERSEQUENCE_HPP_ diff --git a/include/estd/IntrusiveForwardList.hpp b/include/estd/IntrusiveForwardList.hpp new file mode 100644 index 0000000..faa98d1 --- /dev/null +++ b/include/estd/IntrusiveForwardList.hpp @@ -0,0 +1,1120 @@ +/** + * \file + * \brief IntrusiveForwardList template class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_INTRUSIVEFORWARDLIST_HPP_ +#define ESTD_INTRUSIVEFORWARDLIST_HPP_ + +#include <iterator> + +#include <cstddef> + +namespace estd +{ + +namespace internal +{ + +class IntrusiveForwardListBase; + +} + +/** + * \brief IntrusiveForwardListNode class is the node that is needed for the object to be linked in IntrusiveForwardList + * + * To some extent, this class can be considered to be a limited (raw) iterator. + * + * The object that wants to be linked in IntrusiveForwardList must contain a variable of this type - one for each + * intrusive forward list that will be used with object. + */ + +class IntrusiveForwardListNode +{ +public: + + /// AccessKey class is used to limit access to IntrusiveForwardListNode's linkAfter() and unlinkNext() functions - + /// only internal::IntrusiveForwardListBase can link/unlink nodes + class AccessKey + { + friend class internal::IntrusiveForwardListBase; + + /** + * \brief AccessKey's constructor + */ + + constexpr AccessKey() + { + + } + + AccessKey(const AccessKey&) = delete; + AccessKey(AccessKey&&) = delete; + const AccessKey& operator=(const AccessKey&) = delete; + AccessKey& operator=(AccessKey&&) = delete; + }; + + /** + * \brief IntrusiveForwardListNode's constructor + */ + + constexpr IntrusiveForwardListNode() : + nextNode_{} + { + + } + + /** + * \brief IntrusiveForwardListNode's move constructor + * + * \param [in] other is a rvalue reference to IntrusiveForwardListNode used as source of move construction + */ + + IntrusiveForwardListNode(IntrusiveForwardListNode&& other) : + nextNode_{other.nextNode_} + { + other.reset(); + } + + /** + * \return pointer to next node on the list + */ + + IntrusiveForwardListNode* getNextNode() const + { + return nextNode_; + } + + /** + * \return true if the node is linked in some list, false otherwise + */ + + bool isLinked() const + { + return nextNode_ != nullptr; + } + + /** + * \brief Links the node in the list after \a position. + * + * \note Access to this function is restricted only to functions from internal::IntrusiveForwardListBase class + * + * \param [in] position is a pointer to node after which this node will be linked + * \param [in] accessKey is used to limit access to this function + */ + + void linkAfter(IntrusiveForwardListNode* const position, AccessKey) + { + nextNode_ = position->getNextNode(); + position->nextNode_ = this; + } + + /** + * \brief Swaps contents with another node. + * + * \param [in] other is a reference to IntrusiveForwardListNode with which contents of this node will be swapped + */ + + void swap(IntrusiveForwardListNode& other) + { + using std::swap; + swap(nextNode_, other.nextNode_); + } + + /** + * \brief Unlinks the node following this one from the list. + * + * \note Access to this function is restricted only to functions from internal::IntrusiveForwardListBase class + * + * \param [in] accessKey is used to limit access to this function + */ + + void unlinkNext(AccessKey) + { + auto& nextNode = *nextNode_; + nextNode_ = nextNode.nextNode_; + + nextNode.reset(); + } + + IntrusiveForwardListNode(const IntrusiveForwardListNode&) = delete; + const IntrusiveForwardListNode& operator=(const IntrusiveForwardListNode&) = delete; + IntrusiveForwardListNode& operator=(IntrusiveForwardListNode&&) = delete; + +private: + + /** + * \brief Resets the node to the same state as right after construction. + */ + + void reset() + { + nextNode_ = {}; + } + + /// pointer to next node on the list + IntrusiveForwardListNode* nextNode_; +}; + +/** + * \brief Swaps contents of two nodes. + * + * \param [in] left is a reference to IntrusiveForwardListNode with which contents of \a right will be swapped + * \param [in] right is a reference to IntrusiveForwardListNode with which contents of \a left will be swapped + */ + +inline void swap(IntrusiveForwardListNode& left, IntrusiveForwardListNode& right) +{ + left.swap(right); +} + +namespace internal +{ + +/** + * \brief IntrusiveForwardListBase class provides base functionalities for IntrusiveForwardList class, but without any + * knowledge about types + * + * This class tries to provide an interface similar to std::forward_list. + */ + +class IntrusiveForwardListBase +{ +public: + + /** + * \brief IntrusiveForwardListBase's constructor + */ + + constexpr IntrusiveForwardListBase() : + rootNode_{} + { + + } + + /** + * \brief IntrusiveForwardListBase's destructor + * + * Unlinks all nodes from the list. + */ + + ~IntrusiveForwardListBase() + { + clear(); + } + + /** + * \return pointer to "one before the first" node on the list + */ + + IntrusiveForwardListNode* before_begin() + { + return &rootNode_; + } + + /** + * \return const pointer to "one before the first" node on the list + */ + + const IntrusiveForwardListNode* before_begin() const + { + return &rootNode_; + } + + /** + * \return pointer to first node on the list + */ + + IntrusiveForwardListNode* begin() + { + return rootNode_.getNextNode(); + } + + /** + * \return const pointer to first node on the list + */ + + const IntrusiveForwardListNode* begin() const + { + return rootNode_.getNextNode(); + } + + /** + * \return const pointer to "one before the first" node on the list + */ + + const IntrusiveForwardListNode* cbefore_begin() const + { + return before_begin(); + } + + /** + * \return const pointer to first node on the list + */ + + const IntrusiveForwardListNode* cbegin() const + { + return begin(); + } + + /** + * \return const pointer to "one past the last" node on the list + */ + + const IntrusiveForwardListNode* cend() const + { + return end(); + } + + /** + * \brief Unlinks all nodes from the list. + */ + + void clear() + { + while (empty() == false) + pop_front(); + } + + /** + * \return true is the list is empty, false otherwise + */ + + bool empty() const + { + return begin() == end(); + } + + /** + * \return pointer to "one past the last" node on the list + */ + + IntrusiveForwardListNode* end() + { + return nullptr; + } + + /** + * \return const pointer to "one past the last" node on the list + */ + + const IntrusiveForwardListNode* end() const + { + return nullptr; + } + + /** + * \brief Unlinks the first node from the list. + */ + + void pop_front() + { + erase_after(before_begin()); + } + + /** + * \brief Links the node at the beginning of the list. + * + * \param [in] newNode is a reference to node that will be linked in the list + */ + + void push_front(IntrusiveForwardListNode& newNode) + { + insert_after(before_begin(), newNode); + } + + /** + * \brief Swaps contents with another list. + * + * \param [in] other is a reference to IntrusiveForwardListBase with which contents of this list will be swapped + */ + + void swap(IntrusiveForwardListBase& other) + { + rootNode_.swap(other.rootNode_); + } + + /** + * \brief Unlinks the node following \a position from the list. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is a pointer to the node preceding the one which will be unlinked from the list + * + * \return pointer to the node that was following the node which was unlinked + */ + + static IntrusiveForwardListNode* erase_after(IntrusiveForwardListNode* const position) + { + position->unlinkNext({}); + return position->getNextNode(); + } + + /** + * \brief Links the node in the list after \a position. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is a pointer to node after which \a newNode will be linked + * \param [in] newNode is a reference to node that will be linked in the list + */ + + static void insert_after(IntrusiveForwardListNode* const position, IntrusiveForwardListNode& newNode) + { + newNode.linkAfter(position, {}); + } + + /** + * \brief Transfers the node from one list to another list after \a position. + * + * \note No instance of any list is needed for this operation. + * + * \param [in] position is a pointer to node after which spliced node will be linked + * \param [in] beforeSplicedNode is a pointer to node preceding the one which will be spliced from one list to + * another + */ + + static void splice_after(IntrusiveForwardListNode* const position, + IntrusiveForwardListNode* const beforeSplicedNode) + { + const auto splicedNode = beforeSplicedNode->getNextNode(); + erase_after(beforeSplicedNode); + insert_after(position, *splicedNode); + } + + IntrusiveForwardListBase(const IntrusiveForwardListBase&) = delete; + IntrusiveForwardListBase(IntrusiveForwardListBase&&) = default; + const IntrusiveForwardListBase& operator=(const IntrusiveForwardListBase&) = delete; + IntrusiveForwardListBase& operator=(IntrusiveForwardListBase&&) = delete; + +private: + + /// root node of the intrusive forward list + IntrusiveForwardListNode rootNode_; +}; + +/** + * \brief Swaps contents of two lists. + * + * \param [in] left is a reference to IntrusiveForwardListBase with which contents of \a right will be swapped + * \param [in] right is a reference to IntrusiveForwardListBase with which contents of \a left will be swapped + */ + +inline void swap(IntrusiveForwardListBase& left, IntrusiveForwardListBase& right) +{ + left.swap(right); +} + +} // namespace internal + +/** + * \brief IntrusiveForwardListIterator class is an iterator of elements on IntrusiveForwardList. + * + * This class provides an interface similar to std::forward_list::iterator. + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + */ + +template<typename T, IntrusiveForwardListNode T::* NodePointer, typename U = T> +class IntrusiveForwardListIterator +{ +public: + + /// difference type + using difference_type = ptrdiff_t; + + /// category of the iterator + using iterator_category = std::forward_iterator_tag; + + /// pointer to object "pointed to" by the iterator + using pointer = U*; + + /// reference to object "pointed to" by the iterator + using reference = U&; + + /// value "pointed to" by the iterator + using value_type = U; + + /** + * \brief IntrusiveForwardListIterator's constructor + */ + + constexpr IntrusiveForwardListIterator() : + node_{} + { + + } + + /** + * \brief IntrusiveForwardListIterator's constructor + * + * \param [in] node is a pointer to IntrusiveForwardListNode of element that will be "pointed to" by the iterator + */ + + constexpr explicit IntrusiveForwardListIterator(IntrusiveForwardListNode* const node) : + node_{node} + { + + } + + /** + * \brief IntrusiveForwardListIterator's constructor + * + * \param [in] element is a reference to element that will be "pointed to" by the iterator + */ + + constexpr explicit IntrusiveForwardListIterator(reference element) : + node_{&(element.*NodePointer)} + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + } + + /** + * \brief IntrusiveForwardListIterator's binary infix pointer member access operator + * + * \return pointer to object "pointed to" by the iterator + */ + + pointer operator->() const + { + return getPointer(); + } + + /** + * \brief IntrusiveForwardListIterator's unary prefix dereference operator + * + * \return reference to object "pointed to" by the iterator + */ + + reference operator*() const + { + return *getPointer(); + } + + /** + * \brief IntrusiveForwardListIterator's unary prefix increment operator + * + * \return reference to "this" iterator + */ + + IntrusiveForwardListIterator& operator++() + { + node_ = node_->getNextNode(); + return *this; + } + + /** + * \brief IntrusiveForwardListIterator's unary postfix increment operator + * + * \return copy of "this" iterator before increment + */ + + IntrusiveForwardListIterator operator++(int) + { + const auto temporary = *this; + node_ = node_->getNextNode(); + return temporary; + } + + /** + * \brief IntrusiveForwardListIterator's "equal to" comparison operator + * + * \param [in] other is a const reference to IntrusiveForwardListIterator on right-hand side of equality operator + * + * \return true if both iterators are equal, false otherwise + */ + + bool operator==(const IntrusiveForwardListIterator& other) const + { + return node_ == other.node_; + } + +private: + + /** + * \brief Converts contained pointer to IntrusiveForwardListNode to pointer to object that contains this node. + * + * \return pointer to object "pointed to" by the iterator + */ + + pointer getPointer() const + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + + const auto offset = reinterpret_cast<size_t>(&(static_cast<pointer>(nullptr)->*NodePointer)); + return reinterpret_cast<pointer>(reinterpret_cast<size_t>(node_) - offset); + } + + /// pointer to IntrusiveForwardListNode of the object "pointed to" by the iterator + IntrusiveForwardListNode* node_; +}; + +/** + * \brief IntrusiveForwardListIterator's "not equal to" comparison operator + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveForwardListIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveForwardListIterator on right-hand side of comparison operator + * + * \return true if iterators are not equal, false otherwise + */ + +template<typename T, IntrusiveForwardListNode T::* NodePointer, typename U = T> +inline bool operator!=(const IntrusiveForwardListIterator<T, NodePointer, U>& left, + const IntrusiveForwardListIterator<T, NodePointer, U>& right) +{ + return (left == right) == false; +} + +/** + * \brief IntrusiveForwardListConstIterator class is a const iterator of elements on IntrusiveForwardList. + * + * This class provides an interface similar to std::forward_list::const_iterator. + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a const pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + */ + +template<typename T, const IntrusiveForwardListNode T::* NodePointer, typename U = T> +class IntrusiveForwardListConstIterator +{ +public: + + /// difference type + using difference_type = ptrdiff_t; + + /// category of the iterator + using iterator_category = std::forward_iterator_tag; + + /// pointer to object "pointed to" by the iterator + using pointer = const U*; + + /// reference to object "pointed to" by the iterator + using reference = const U&; + + /// value "pointed to" by the iterator + using value_type = U; + + /** + * \brief IntrusiveForwardListConstIterator's constructor + */ + + constexpr IntrusiveForwardListConstIterator() : + node_{} + { + + } + + /** + * \brief IntrusiveForwardListConstIterator's constructor + * + * \param [in] node is a pointer to const IntrusiveForwardListNode of element that will be "pointed to" by the + * iterator + */ + + constexpr explicit IntrusiveForwardListConstIterator(const IntrusiveForwardListNode* const node) : + node_{node} + { + + } + + /** + * \brief IntrusiveForwardListConstIterator's constructor + * + * \param [in] element is a const reference to element that will be "pointed to" by the iterator + */ + + constexpr explicit IntrusiveForwardListConstIterator(reference element) : + node_{&(element.*NodePointer)} + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + } + + /** + * \brief IntrusiveForwardListConstIterator's constructor + * + * Converts non-const iterator (IntrusiveForwardListIterator) to const iterator (IntrusiveForwardListConstIterator). + * + * \tparam NonConstNodePointer is a non-const version of \a NodePointer + * + * \param [in] iterator is a const reference to non-const iterator (IntrusiveForwardListIterator) + */ + + template<IntrusiveForwardListNode T::* NonConstNodePointer> + constexpr + IntrusiveForwardListConstIterator(const IntrusiveForwardListIterator<T, NonConstNodePointer, U>& iterator) : + IntrusiveForwardListConstIterator{*iterator} + { + + } + + /** + * \brief IntrusiveForwardListConstIterator's binary infix pointer member access operator + * + * \return pointer to object "pointed to" by the iterator + */ + + pointer operator->() const + { + return getPointer(); + } + + /** + * \brief IntrusiveForwardListConstIterator's unary prefix dereference operator + * + * \return reference to object "pointed to" by the iterator + */ + + reference operator*() const + { + return *getPointer(); + } + + /** + * \brief IntrusiveForwardListConstIterator's unary prefix increment operator + * + * \return reference to "this" iterator + */ + + IntrusiveForwardListConstIterator& operator++() + { + node_ = node_->getNextNode(); + return *this; + } + + /** + * \brief IntrusiveForwardListConstIterator's unary postfix increment operator + * + * \return copy of "this" iterator before increment + */ + + IntrusiveForwardListConstIterator operator++(int) + { + const auto temporary = *this; + node_ = node_->getNextNode(); + return temporary; + } + + /** + * \brief IntrusiveForwardListConstIterator's "equal to" comparison operator + * + * \param [in] other is a const reference to IntrusiveForwardListConstIterator on right-hand side of equality + * operator + * + * \return true if both iterators are equal, false otherwise + */ + + bool operator==(const IntrusiveForwardListConstIterator& other) const + { + return node_ == other.node_; + } + +private: + + /** + * \brief Converts contained pointer to IntrusiveForwardListNode to pointer to object that contains this node. + * + * \return pointer to object "pointed to" by the iterator + */ + + pointer getPointer() const + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + + const auto offset = reinterpret_cast<size_t>(&(static_cast<pointer>(nullptr)->*NodePointer)); + return reinterpret_cast<pointer>(reinterpret_cast<size_t>(node_) - offset); + } + + /// pointer to const IntrusiveForwardListNode of the object "pointed to" by the iterator + const IntrusiveForwardListNode* node_; +}; + +/** + * \brief IntrusiveForwardListConstIterator's "not equal to" comparison operator + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a const pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveForwardListConstIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveForwardListConstIterator on right-hand side of comparison operator + * + * \return true if iterators are not equal, false otherwise + */ + +template<typename T, const IntrusiveForwardListNode T::* NodePointer, typename U = T> +inline bool operator!=(const IntrusiveForwardListConstIterator<T, NodePointer, U>& left, + const IntrusiveForwardListConstIterator<T, NodePointer, U>& right) +{ + return (left == right) == false; +} + +/** + * \brief "Equal to" comparison operator for IntrusiveForwardListIterator and IntrusiveForwardListConstIterator + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveForwardListIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveForwardListConstIterator on right-hand side of comparison operator + * + * \return true if both iterators are equal, false otherwise + */ + +template<typename T, IntrusiveForwardListNode T::* NodePointer, const IntrusiveForwardListNode T::* ConstNodePointer, + typename U = T> +inline bool operator==(const IntrusiveForwardListIterator<T, NodePointer, U>& left, + const IntrusiveForwardListConstIterator<T, ConstNodePointer, U>& right) +{ + return decltype(right){left} == right; +} + +/** + * \brief "Not equal to" comparison operator for IntrusiveForwardListIterator and IntrusiveForwardListConstIterator + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveForwardListIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveForwardListConstIterator on right-hand side of comparison operator + * + * \return true if iterators are not equal, false otherwise + */ + +template<typename T, IntrusiveForwardListNode T::* NodePointer, const IntrusiveForwardListNode T::* ConstNodePointer, + typename U = T> +inline bool operator!=(const IntrusiveForwardListIterator<T, NodePointer, U>& left, + const IntrusiveForwardListConstIterator<T, ConstNodePointer, U>& right) +{ + return (left == right) == false; +} + +/** + * \brief "Not equal to" comparison operator for IntrusiveForwardListConstIterator and IntrusiveForwardListIterator + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveForwardListConstIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveForwardListIterator on right-hand side of comparison operator + * + * \return true if iterators are not equal, false otherwise + */ + +template<typename T, IntrusiveForwardListNode T::* NodePointer, const IntrusiveForwardListNode T::* ConstNodePointer, + typename U = T> +inline bool operator!=(const IntrusiveForwardListConstIterator<T, ConstNodePointer, U>& left, + const IntrusiveForwardListIterator<T, NodePointer, U>& right) +{ + return right != left; +} + +/** + * \brief IntrusiveForwardList class is an intrusive linear singly linked list. + * + * This class tries to provide an interface similar to std::forward_list. + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; using different type than \a T can be used + * to break circular dependencies, because \a T must be fully defined to instantiate this class, but it is enough to + * forward declare \a U - it only needs to be fully defined to use member functions + */ + +template<typename T, IntrusiveForwardListNode T::* NodePointer, typename U = T> +class IntrusiveForwardList +{ +public: + + /// const iterator of elements on the list + using const_iterator = IntrusiveForwardListConstIterator<T, NodePointer, U>; + + /// const pointer to value linked in the list + using const_pointer = const U*; + + /// const reference to value linked in the list + using const_reference = const U&; + + /// iterator of elements on the list + using iterator = IntrusiveForwardListIterator<T, NodePointer, U>; + + /// pointer to value linked in the list + using pointer = U*; + + /// reference to value linked in the list + using reference = U&; + + /// value linked in the list + using value_type = U; + + /** + * \brief IntrusiveForwardList's constructor + */ + + constexpr IntrusiveForwardList() : + intrusiveForwardListBase_{} + { + + } + + /** + * \return iterator of "one before the first" element on the list + */ + + iterator before_begin() + { + return iterator{intrusiveForwardListBase_.before_begin()}; + } + + /** + * \return const iterator of "one before the first" element on the list + */ + + const_iterator before_begin() const + { + return const_iterator{intrusiveForwardListBase_.before_begin()}; + } + + /** + * \return iterator of first element on the list + */ + + iterator begin() + { + return iterator{intrusiveForwardListBase_.begin()}; + } + + /** + * \return const iterator of first element on the list + */ + + const_iterator begin() const + { + return const_iterator{intrusiveForwardListBase_.begin()}; + } + + /** + * \return const iterator of "one before the first" element on the list + */ + + const_iterator cbefore_begin() const + { + return before_begin(); + } + + /** + * \return const iterator of first element on the list + */ + + const_iterator cbegin() const + { + return begin(); + } + + /** + * \return const iterator of "one past the last" element on the list + */ + + const_iterator cend() const + { + return end(); + } + + /** + * \brief Unlinks all elements from the list. + */ + + void clear() + { + intrusiveForwardListBase_.clear(); + } + + /** + * \return true is the list is empty, false otherwise + */ + + bool empty() const + { + return intrusiveForwardListBase_.empty(); + } + + /** + * \return iterator of "one past the last" element on the list + */ + + iterator end() + { + return iterator{intrusiveForwardListBase_.end()}; + } + + /** + * \return const iterator of "one past the last" element on the list + */ + + const_iterator end() const + { + return const_iterator{intrusiveForwardListBase_.end()}; + } + + /** + * \return reference to first element on the list + */ + + reference front() + { + return *begin(); + } + + /** + * \return const reference to first element on the list + */ + + const_reference front() const + { + return *begin(); + } + + /** + * \brief Unlinks the first element from the list. + */ + + void pop_front() + { + erase_after(before_begin()); + } + + /** + * \brief Links the element at the beginning of the list. + * + * \param [in] newElement is a reference to the element that will be linked in the list + */ + + void push_front(reference newElement) + { + insert_after(before_begin(), newElement); + } + + /** + * \brief Swaps contents with another list. + * + * \param [in] other is a reference to IntrusiveForwardList with which contents of this list will be swapped + */ + + void swap(IntrusiveForwardList& other) + { + intrusiveForwardListBase_.swap(other.intrusiveForwardListBase_); + } + + /** + * \brief Unlinks the element following \a position from the list. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is an iterator preceding the element which will be unlinked from the list + * + * \return iterator of the element that was following the element which was unlinked + */ + + static iterator erase_after(const iterator position) + { + auto& positionNode = (*position).*NodePointer; + const auto afterNextNode = internal::IntrusiveForwardListBase::erase_after(&positionNode); + return iterator{afterNextNode}; + } + + /** + * \brief Links the element in the list after \a position. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is an iterator of the element after which \a newNode will be linked + * \param [in] newElement is a reference to the element that will be linked in the list + * + * \return iterator of \a newElement + */ + + static iterator insert_after(const iterator position, reference newElement) + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + + auto& positionNode = (*position).*NodePointer; + auto& newElementNode = newElement.*NodePointer; + internal::IntrusiveForwardListBase::insert_after(&positionNode, newElementNode); + return iterator{&newElementNode}; + } + + /** + * \brief Transfers the element from one list to another list after \a position. + * + * \note No instance of any list is needed for this operation. + * + * \param [in] position is an iterator of the element after which spliced element will be linked + * \param [in] beforeSplicedElement is an iterator of the element preceding the one which will be spliced from one + * list to another + */ + + static void splice_after(const iterator position, const iterator beforeSplicedElement) + { + auto& positionNode = (*position).*NodePointer; + auto& beforeSplicedElementNode = (*beforeSplicedElement).*NodePointer; + internal::IntrusiveForwardListBase::splice_after(&positionNode, &beforeSplicedElementNode); + } + + IntrusiveForwardList(const IntrusiveForwardList&) = delete; + IntrusiveForwardList(IntrusiveForwardList&&) = default; + const IntrusiveForwardList& operator=(const IntrusiveForwardList&) = delete; + IntrusiveForwardList& operator=(IntrusiveForwardList&&) = delete; + +private: + + /// internal IntrusiveForwardListBase object + internal::IntrusiveForwardListBase intrusiveForwardListBase_; +}; + +/** + * \brief Swaps contents of two lists. + * + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a reference to IntrusiveForwardList with which contents of \a right will be swapped + * \param [in] right is a reference to IntrusiveForwardList with which contents of \a left will be swapped + */ + +template<typename T, IntrusiveForwardListNode T::* NodePointer, typename U = T> +inline void swap(IntrusiveForwardList<T, NodePointer, U>& left, IntrusiveForwardList<T, NodePointer, U>& right) +{ + left.swap(right); +} + +} // namespace estd + +#endif // ESTD_INTRUSIVEFORWARDLIST_HPP_ diff --git a/include/estd/IntrusiveList.hpp b/include/estd/IntrusiveList.hpp new file mode 100644 index 0000000..82597f4 --- /dev/null +++ b/include/estd/IntrusiveList.hpp @@ -0,0 +1,1208 @@ +/** + * \file + * \brief IntrusiveList template class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_INTRUSIVELIST_HPP_ +#define ESTD_INTRUSIVELIST_HPP_ + +#include <iterator> + +#include <cstddef> + +namespace estd +{ + +namespace internal +{ + +class IntrusiveListBase; + +} + +/** + * \brief IntrusiveListNode class is the node that is needed for the object to be linked in IntrusiveList + * + * To some extent, this class can be considered to be a limited (raw) iterator. + * + * The object that wants to be linked in IntrusiveList must contain a variable of this type - one for each intrusive + * list that will be used with object. + */ + +class IntrusiveListNode +{ +public: + + /// LinkAccessKey class is used to limit access to IntrusiveListNode::link() function - only + /// internal::IntrusiveListBase can link nodes to the list + class LinkAccessKey + { + friend class internal::IntrusiveListBase; + + /** + * \brief LinkAccessKey's constructor + */ + + constexpr LinkAccessKey() + { + + } + + LinkAccessKey(const LinkAccessKey&) = delete; + LinkAccessKey(LinkAccessKey&&) = delete; + const LinkAccessKey& operator=(const LinkAccessKey&) = delete; + LinkAccessKey& operator=(LinkAccessKey&&) = delete; + }; + + /** + * \brief IntrusiveListNode's constructor + */ + + constexpr IntrusiveListNode() : + nextNode_{this}, + previousNode_{this} + { + + } + + /** + * \brief IntrusiveListNode's move constructor + * + * \param [in] other is a rvalue reference to IntrusiveListNode used as source of move construction + */ + + IntrusiveListNode(IntrusiveListNode&& other) + { + if (other.isLinked() == false) + { + reset(); + return; + } + + nextNode_ = other.nextNode_; + previousNode_ = other.previousNode_; + nextNode_->previousNode_ = previousNode_->nextNode_ = this; + other.reset(); + } + + /** + * \brief IntrusiveListNode's destructor + * + * Unlinks the node from the list. + */ + + ~IntrusiveListNode() + { + unlink(); + } + + /** + * \return reference to next node on the list + */ + + IntrusiveListNode& getNextNode() const + { + return *nextNode_; + } + + /** + * \return reference to previous node on the list + */ + + IntrusiveListNode& getPreviousNode() const + { + return *previousNode_; + } + + /** + * \return true if the node is linked in some list, false otherwise + */ + + bool isLinked() const + { + return nextNode_ != this; + } + + /** + * \brief Links the node in the list before \a position. + * + * \note Access to this function is restricted only to functions from internal::IntrusiveListBase class + * + * \param [in] position is a reference to node before which this node will be linked + * \param [in] linkAccessKey is used to limit access to this function + */ + + void link(IntrusiveListNode& position, LinkAccessKey) + { + unlink(); + + nextNode_ = &position; + previousNode_ = position.previousNode_; + position.previousNode_->nextNode_ = this; + position.previousNode_ = this; + } + + /** + * \brief Swaps contents with another node. + * + * \param [in] other is a reference to IntrusiveListNode with which contents of this node will be swapped + */ + + void swap(IntrusiveListNode& other) + { + const auto thisWasLinked = isLinked(); + const auto otherWasLinked = other.isLinked(); + + if (thisWasLinked == true || otherWasLinked == true) + { + using std::swap; + swap(nextNode_, other.nextNode_); + swap(previousNode_, other.previousNode_); + + if (thisWasLinked == true) + other.nextNode_->previousNode_ = other.previousNode_->nextNode_ = &other; + else + other.reset(); + + if (otherWasLinked == true) + nextNode_->previousNode_ = previousNode_->nextNode_ = this; + else + reset(); + } + } + + /** + * \brief Unlinks the node from the list. + */ + + void unlink() + { + previousNode_->nextNode_ = nextNode_; + nextNode_->previousNode_ = previousNode_; + + reset(); + } + + IntrusiveListNode(const IntrusiveListNode&) = delete; + const IntrusiveListNode& operator=(const IntrusiveListNode&) = delete; + IntrusiveListNode& operator=(IntrusiveListNode&&) = delete; + +private: + + /** + * \brief Resets the node to the same state as right after construction. + */ + + void reset() + { + nextNode_ = this; + previousNode_ = this; + } + + /// reference to next node on the list + IntrusiveListNode* nextNode_; + + /// reference to previous node on the list + IntrusiveListNode* previousNode_; +}; + +namespace internal +{ + +/** + * \brief IntrusiveListBase class provides base functionalities for IntrusiveList class, but without any knowledge about + * types + * + * This class tries to provide an interface similar to std::list. + */ + +class IntrusiveListBase +{ +public: + + /** + * \brief IntrusiveListBase's constructor + */ + + constexpr IntrusiveListBase() : + rootNode_{} + { + + } + + /** + * \brief IntrusiveListBase's destructor + * + * Unlinks all nodes from the list. + */ + + ~IntrusiveListBase() + { + clear(); + } + + /** + * \return reference to first node on the list + */ + + IntrusiveListNode& begin() + { + return rootNode_.getNextNode(); + } + + /** + * \return const reference to first node on the list + */ + + const IntrusiveListNode& begin() const + { + return rootNode_.getNextNode(); + } + + /** + * \return const reference to first node on the list + */ + + const IntrusiveListNode& cbegin() const + { + return begin(); + } + + /** + * \return const reference to "one past the last" node on the list + */ + + const IntrusiveListNode& cend() const + { + return end(); + } + + /** + * \brief Unlinks all nodes from the list. + */ + + void clear() + { + while (empty() == false) + pop_front(); + } + + /** + * \return true is the list is empty, false otherwise + */ + + bool empty() const + { + return &begin() == &end(); + } + + /** + * \return reference to "one past the last" node on the list + */ + + IntrusiveListNode& end() + { + return rootNode_; + } + + /** + * \return const reference to "one past the last" node on the list + */ + + const IntrusiveListNode& end() const + { + return rootNode_; + } + + /** + * \brief Unlinks the last node from the list. + */ + + void pop_back() + { + erase(end().getPreviousNode()); + } + + /** + * \brief Unlinks the first node from the list. + */ + + void pop_front() + { + erase(begin()); + } + + /** + * \brief Links the node at the end of the list. + * + * \param [in] newNode is a reference to node that will be linked in the list + */ + + void push_back(IntrusiveListNode& newNode) + { + insert(end(), newNode); + } + + /** + * \brief Links the node at the beginning of the list. + * + * \param [in] newNode is a reference to node that will be linked in the list + */ + + void push_front(IntrusiveListNode& newNode) + { + insert(begin(), newNode); + } + + /** + * \brief Swaps contents with another list. + * + * \param [in] other is a reference to IntrusiveListBase with which contents of this list will be swapped + */ + + void swap(IntrusiveListBase& other) + { + rootNode_.swap(other.rootNode_); + } + + /** + * \brief Unlinks the node at \a position from the list. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is a reference to the node that will be unlinked from the list + * + * \return reference to the node that was following the node which was unlinked + */ + + static IntrusiveListNode& erase(IntrusiveListNode& position) + { + auto& next = position.getNextNode(); + position.unlink(); + return next; + } + + /** + * \brief Links the node in the list before \a position. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is a reference to node before which \a newNode will be linked + * \param [in] newNode is a reference to node that will be linked in the list + */ + + static void insert(IntrusiveListNode& position, IntrusiveListNode& newNode) + { + newNode.link(position, {}); + } + + /** + * \brief Transfers the node from one list to another list before \a position. + * + * \note No instance of any list is needed for this operation. + * + * \param [in] position is a reference to node before which \a splicedNode will be linked + * \param [in] splicedNode is a reference to node that will be spliced from one list to another + */ + + static void splice(IntrusiveListNode& position, IntrusiveListNode& splicedNode) + { + insert(position, splicedNode); + } + + IntrusiveListBase(const IntrusiveListBase&) = delete; + IntrusiveListBase(IntrusiveListBase&&) = default; + const IntrusiveListBase& operator=(const IntrusiveListBase&) = delete; + IntrusiveListBase& operator=(IntrusiveListBase&&) = delete; + +private: + + /// root node of the intrusive list + IntrusiveListNode rootNode_; +}; + +/** + * \brief Swaps contents of two lists. + * + * \param [in] left is a reference to IntrusiveListBase with which contents of \a right will be swapped + * \param [in] right is a reference to IntrusiveListBase with which contents of \a left will be swapped + */ + +inline void swap(IntrusiveListBase& left, IntrusiveListBase& right) +{ + left.swap(right); +} + +} // namespace internal + +/** + * \brief IntrusiveListIterator class is an iterator of elements on IntrusiveList. + * + * This class provides an interface similar to std::list::iterator. + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + */ + +template<typename T, IntrusiveListNode T::* NodePointer, typename U = T> +class IntrusiveListIterator +{ +public: + + /// difference type + using difference_type = ptrdiff_t; + + /// category of the iterator + using iterator_category = std::bidirectional_iterator_tag; + + /// pointer to object "pointed to" by the iterator + using pointer = U*; + + /// reference to object "pointed to" by the iterator + using reference = U&; + + /// value "pointed to" by the iterator + using value_type = U; + + /** + * \brief IntrusiveListIterator's constructor + */ + + constexpr IntrusiveListIterator() : + node_{} + { + + } + + /** + * \brief IntrusiveListIterator's constructor + * + * \param [in] node is a pointer to IntrusiveListNode of element that will be "pointed to" by the iterator + */ + + constexpr explicit IntrusiveListIterator(IntrusiveListNode* const node) : + node_{node} + { + + } + + /** + * \brief IntrusiveListIterator's constructor + * + * \param [in] element is a reference to element that will be "pointed to" by the iterator + */ + + constexpr explicit IntrusiveListIterator(reference element) : + node_{&(element.*NodePointer)} + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + } + + /** + * \brief IntrusiveListIterator's binary infix pointer member access operator + * + * \return pointer to object "pointed to" by the iterator + */ + + pointer operator->() const + { + return getPointer(); + } + + /** + * \brief IntrusiveListIterator's unary prefix dereference operator + * + * \return reference to object "pointed to" by the iterator + */ + + reference operator*() const + { + return *getPointer(); + } + + /** + * \brief IntrusiveListIterator's unary prefix increment operator + * + * \return reference to "this" iterator + */ + + IntrusiveListIterator& operator++() + { + node_ = &node_->getNextNode(); + return *this; + } + + /** + * \brief IntrusiveListIterator's unary postfix increment operator + * + * \return copy of "this" iterator before increment + */ + + IntrusiveListIterator operator++(int) + { + const auto temporary = *this; + node_ = &node_->getNextNode(); + return temporary; + } + + /** + * \brief IntrusiveListIterator's unary prefix decrement operator + * + * \return reference to "this" iterator + */ + + IntrusiveListIterator& operator--() + { + node_ = &node_->getPreviousNode(); + return *this; + } + + /** + * \brief IntrusiveListIterator's unary postfix decrement operator + * + * \return copy of "this" iterator before decrement + */ + + IntrusiveListIterator operator--(int) + { + const auto temporary = *this; + node_ = &node_->getPreviousNode(); + return temporary; + } + + /** + * \brief IntrusiveListIterator's "equal to" comparison operator + * + * \param [in] other is a const reference to IntrusiveListIterator on right-hand side of comparison operator + * + * \return true if both iterators are equal, false otherwise + */ + + bool operator==(const IntrusiveListIterator& other) const + { + return node_ == other.node_; + } + +private: + + /** + * \brief Converts contained pointer to IntrusiveListNode to pointer to object that contains this node. + * + * \return pointer to object "pointed to" by the iterator + */ + + pointer getPointer() const + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + + const auto offset = reinterpret_cast<size_t>(&(static_cast<pointer>(nullptr)->*NodePointer)); + return reinterpret_cast<pointer>(reinterpret_cast<size_t>(node_) - offset); + } + + /// pointer to IntrusiveListNode of the object "pointed to" by the iterator + IntrusiveListNode* node_; +}; + +/** + * \brief IntrusiveListIterator's "not equal to" comparison operator + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveListIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveListIterator on right-hand side of comparison operator + * + * \return true if iterators are not equal, false otherwise + */ + +template<typename T, IntrusiveListNode T::* NodePointer, typename U = T> +inline bool operator!=(const IntrusiveListIterator<T, NodePointer, U>& left, + const IntrusiveListIterator<T, NodePointer, U>& right) +{ + return (left == right) == false; +} + +/** + * \brief IntrusiveListConstIterator class is a const iterator of elements on IntrusiveList. + * + * This class provides an interface similar to std::list::const_iterator. + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + */ + +template<typename T, const IntrusiveListNode T::* NodePointer, typename U = T> +class IntrusiveListConstIterator +{ +public: + + /// difference type + using difference_type = ptrdiff_t; + + /// category of the iterator + using iterator_category = std::bidirectional_iterator_tag; + + /// pointer to object "pointed to" by the iterator + using pointer = const U*; + + /// reference to object "pointed to" by the iterator + using reference = const U&; + + /// value "pointed to" by the iterator + using value_type = U; + + /** + * \brief IntrusiveListConstIterator's constructor + */ + + constexpr IntrusiveListConstIterator() : + node_{} + { + + } + + /** + * \brief IntrusiveListConstIterator's constructor + * + * \param [in] node is a pointer to const IntrusiveListNode of element that will be "pointed to" by the iterator + */ + + constexpr explicit IntrusiveListConstIterator(const IntrusiveListNode* const node) : + node_{node} + { + + } + + /** + * \brief IntrusiveListConstIterator's constructor + * + * \param [in] element is a const reference to element that will be "pointed to" by the iterator + */ + + constexpr explicit IntrusiveListConstIterator(reference element) : + node_{&(element.*NodePointer)} + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + } + + /** + * \brief IntrusiveListConstIterator's constructor + * + * Converts non-const iterator (IntrusiveListIterator) to const iterator (IntrusiveListConstIterator). + * + * \tparam NonConstNodePointer is a non-const version of \a NodePointer + * + * \param [in] iterator is a const reference to non-const iterator (IntrusiveListIterator) + */ + + template<IntrusiveListNode T::* NonConstNodePointer> + constexpr IntrusiveListConstIterator(const IntrusiveListIterator<T, NonConstNodePointer, U>& iterator) : + IntrusiveListConstIterator{*iterator} + { + + } + + /** + * \brief IntrusiveListConstIterator's binary infix pointer member access operator + * + * \return pointer to object "pointed to" by the iterator + */ + + pointer operator->() const + { + return getPointer(); + } + + /** + * \brief IntrusiveListConstIterator's unary prefix dereference operator + * + * \return reference to object "pointed to" by the iterator + */ + + reference operator*() const + { + return *getPointer(); + } + + /** + * \brief IntrusiveListConstIterator's unary prefix increment operator + * + * \return reference to "this" iterator + */ + + IntrusiveListConstIterator& operator++() + { + node_ = &node_->getNextNode(); + return *this; + } + + /** + * \brief IntrusiveListConstIterator's unary postfix increment operator + * + * \return copy of "this" iterator before increment + */ + + IntrusiveListConstIterator operator++(int) + { + const auto temporary = *this; + node_ = &node_->getNextNode(); + return temporary; + } + + /** + * \brief IntrusiveListConstIterator's unary prefix decrement operator + * + * \return reference to "this" iterator + */ + + IntrusiveListConstIterator& operator--() + { + node_ = &node_->getPreviousNode(); + return *this; + } + + /** + * \brief IntrusiveListConstIterator's unary postfix decrement operator + * + * \return copy of "this" iterator before decrement + */ + + IntrusiveListConstIterator operator--(int) + { + const auto temporary = *this; + node_ = &node_->getPreviousNode(); + return temporary; + } + + /** + * \brief IntrusiveListConstIterator's "equal to" comparison operator + * + * \param [in] other is a const reference to IntrusiveListConstIterator on right-hand side of comparison operator + * + * \return true if both iterators are equal, false otherwise + */ + + bool operator==(const IntrusiveListConstIterator& other) const + { + return node_ == other.node_; + } + +private: + + /** + * \brief Converts contained pointer to IntrusiveListNode to pointer to object that contains this node. + * + * \return pointer to object "pointed to" by the iterator + */ + + pointer getPointer() const + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + + const auto offset = reinterpret_cast<size_t>(&(static_cast<pointer>(nullptr)->*NodePointer)); + return reinterpret_cast<pointer>(reinterpret_cast<size_t>(node_) - offset); + } + + /// pointer to const IntrusiveListNode of the object "pointed to" by the iterator + const IntrusiveListNode* node_; +}; + +/** + * \brief IntrusiveListConstIterator's "not equal to" comparison operator + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveListConstIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveListConstIterator on right-hand side of comparison operator + * + * \return true if iterators are not equal, false otherwise + */ + +template<typename T, const IntrusiveListNode T::* NodePointer, typename U = T> +inline bool operator!=(const IntrusiveListConstIterator<T, NodePointer, U>& left, + const IntrusiveListConstIterator<T, NodePointer, U>& right) +{ + return (left == right) == false; +} + +/** + * \brief "Equal to" comparison operator for IntrusiveListIterator and IntrusiveListConstIterator + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveListIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveListConstIterator on right-hand side of comparison operator + * + * \return true if both iterators are equal, false otherwise + */ + +template<typename T, IntrusiveListNode T::* NodePointer, const IntrusiveListNode T::* ConstNodePointer, typename U = T> +inline bool operator==(const IntrusiveListIterator<T, NodePointer, U>& left, + const IntrusiveListConstIterator<T, ConstNodePointer, U>& right) +{ + return decltype(right){left} == right; +} + +/** + * \brief "Not equal to" comparison operator for IntrusiveListIterator and IntrusiveListConstIterator + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveListIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveListConstIterator on right-hand side of comparison operator + * + * \return true if iterators are not equal, false otherwise + */ + +template<typename T, IntrusiveListNode T::* NodePointer, const IntrusiveListNode T::* ConstNodePointer, typename U = T> +inline bool operator!=(const IntrusiveListIterator<T, NodePointer, U>& left, + const IntrusiveListConstIterator<T, ConstNodePointer, U>& right) +{ + return (left == right) == false; +} + +/** + * \brief "Not equal to" comparison operator for IntrusiveListConstIterator and IntrusiveListIterator + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam ConstNodePointer is a const pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a const reference to IntrusiveListConstIterator on left-hand side of comparison operator + * \param [in] right is a const reference to IntrusiveListIterator on right-hand side of comparison operator + * + * \return true if iterators are not equal, false otherwise + */ + +template<typename T, IntrusiveListNode T::* NodePointer, const IntrusiveListNode T::* ConstNodePointer, typename U = T> +inline bool operator!=(const IntrusiveListConstIterator<T, ConstNodePointer, U>& left, + const IntrusiveListIterator<T, NodePointer, U>& right) +{ + return right != left; +} + +/** + * \brief IntrusiveList class is an intrusive circular doubly linked list. + * + * This class tries to provide an interface similar to std::list. + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; using different type than \a T can be used + * to break circular dependencies, because \a T must be fully defined to instantiate this class, but it is enough to + * forward declare \a U - it only needs to be fully defined to use member functions + */ + +template<typename T, IntrusiveListNode T::* NodePointer, typename U = T> +class IntrusiveList +{ +public: + + /// const iterator of elements on the list + using const_iterator = IntrusiveListConstIterator<T, NodePointer, U>; + + /// const reverse iterator of elements on the list + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + /// const pointer to value linked in the list + using const_pointer = const U*; + + /// const reference to value linked in the list + using const_reference = const U&; + + /// iterator of elements on the list + using iterator = IntrusiveListIterator<T, NodePointer, U>; + + /// reverse iterator of elements on the list + using reverse_iterator = std::reverse_iterator<iterator>; + + /// pointer to value linked in the list + using pointer = U*; + + /// reference to value linked in the list + using reference = U&; + + /// value linked in the list + using value_type = U; + + /** + * \brief IntrusiveList's constructor + */ + + constexpr IntrusiveList() : + intrusiveListBase_{} + { + + } + + /** + * \return reference to last element on the list + */ + + reference back() + { + return *--end(); + } + + /** + * \return const reference to last element on the list + */ + + const_reference back() const + { + return *--end(); + } + + /** + * \return iterator of first element on the list + */ + + iterator begin() + { + return iterator{&intrusiveListBase_.begin()}; + } + + /** + * \return const iterator of first element on the list + */ + + const_iterator begin() const + { + return const_iterator{&intrusiveListBase_.begin()}; + } + + /** + * \return const iterator of first element on the list + */ + + const_iterator cbegin() const + { + return begin(); + } + + /** + * \return const iterator of "one past the last" element on the list + */ + + const_iterator cend() const + { + return end(); + } + + /** + * \brief Unlinks all elements from the list. + */ + + void clear() + { + intrusiveListBase_.clear(); + } + + /** + * \return true is the list is empty, false otherwise + */ + + bool empty() const + { + return intrusiveListBase_.empty(); + } + + /** + * \return iterator of "one past the last" element on the list + */ + + iterator end() + { + return iterator{&intrusiveListBase_.end()}; + } + + /** + * \return const iterator of "one past the last" element on the list + */ + + const_iterator end() const + { + return const_iterator{&intrusiveListBase_.end()}; + } + + /** + * \return reference to first element on the list + */ + + reference front() + { + return *begin(); + } + + /** + * \return const reference to first element on the list + */ + + const_reference front() const + { + return *begin(); + } + + /** + * \brief Unlinks the last element from the list. + */ + + void pop_back() + { + erase(--end()); + } + + /** + * \brief Unlinks the first element from the list. + */ + + void pop_front() + { + erase(begin()); + } + + /** + * \brief Links the element at the end of the list. + * + * \param [in] newElement is a reference to the element that will be linked in the list + */ + + void push_back(reference newElement) + { + insert(end(), newElement); + } + + /** + * \brief Links the element at the beginning of the list. + * + * \param [in] newElement is a reference to the element that will be linked in the list + */ + + void push_front(reference newElement) + { + insert(begin(), newElement); + } + + /** + * \brief Swaps contents with another list. + * + * \param [in] other is a reference to IntrusiveList with which contents of this list will be swapped + */ + + void swap(IntrusiveList& other) + { + intrusiveListBase_.swap(other.intrusiveListBase_); + } + + /** + * \brief Unlinks the element at \a position from the list. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is an iterator of the element that will be unlinked from the list + * + * \return iterator of the element that was following the element which was unlinked + */ + + static iterator erase(const iterator position) + { + auto& positionNode = (*position).*NodePointer; + auto& nextNode = internal::IntrusiveListBase::erase(positionNode); + return iterator{&nextNode}; + } + + /** + * \brief Links the element in the list before \a position. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is an iterator of the element before which \a newNode will be linked + * \param [in] newElement is a reference to the element that will be linked in the list + * + * \return iterator of \a newElement + */ + + static iterator insert(const iterator position, reference newElement) + { + static_assert(std::is_convertible<U, T>::value == true, "U must be implicitly convertible to T!"); + + auto& positionNode = (*position).*NodePointer; + auto& newElementNode = newElement.*NodePointer; + internal::IntrusiveListBase::insert(positionNode, newElementNode); + return iterator{&newElementNode}; + } + + /** + * \brief Transfers the element from one list to another list before \a position. + * + * \note No instance of any list is needed for this operation. + * + * \param [in] position is an iterator of the element before which \a splicedElement will be linked + * \param [in] splicedElement is an iterator of the element that will be spliced from one list to another + */ + + static void splice(const iterator position, const iterator splicedElement) + { + auto& positionNode = (*position).*NodePointer; + auto& splicedElementNode = (*splicedElement).*NodePointer; + internal::IntrusiveListBase::splice(positionNode, splicedElementNode); + } + + IntrusiveList(const IntrusiveList&) = delete; + IntrusiveList(IntrusiveList&&) = default; + const IntrusiveList& operator=(const IntrusiveList&) = delete; + IntrusiveList& operator=(IntrusiveList&&) = delete; + +private: + + /// internal IntrusiveListBase object + internal::IntrusiveListBase intrusiveListBase_; +}; + +/** + * \brief Swaps contents of two lists. + * + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a reference to IntrusiveList with which contents of \a right will be swapped + * \param [in] right is a reference to IntrusiveList with which contents of \a left will be swapped + */ + +template<typename T, IntrusiveListNode T::* NodePointer, typename U = T> +inline void swap(IntrusiveList<T, NodePointer, U>& left, IntrusiveList<T, NodePointer, U>& right) +{ + left.swap(right); +} + +} // namespace estd + +#endif // ESTD_INTRUSIVELIST_HPP_ diff --git a/include/estd/ReferenceHolder.hpp b/include/estd/ReferenceHolder.hpp new file mode 100644 index 0000000..2bb4602 --- /dev/null +++ b/include/estd/ReferenceHolder.hpp @@ -0,0 +1,61 @@ +/** + * \file + * \brief ReferenceHolder template class header. + * + * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_REFERENCEHOLDER_HPP_ +#define ESTD_REFERENCEHOLDER_HPP_ + +namespace estd +{ + +/** + * \brief ReferenceHolder template class is a ROMable holder of a reference. + * + * \tparam T is the type of reference held in the object + */ + +template<typename T> +class ReferenceHolder +{ +public: + + /** + * \brief ReferenceHolder constructor. + * + * \param [in] reference is a reference that will be held by the object + */ + + constexpr explicit ReferenceHolder(T& reference) noexcept : + reference_{reference} + { + + } + + /// \return reference held by the object + constexpr operator T&() const noexcept + { + return reference_; + } + + /// \return reference held by the object + constexpr T& get() const noexcept + { + return reference_; + } + +private: + + /// reference held by the object + T& reference_; +}; + +} // namespace estd + +#endif // ESTD_REFERENCEHOLDER_HPP_ diff --git a/include/estd/ReverseAdaptor.hpp b/include/estd/ReverseAdaptor.hpp new file mode 100644 index 0000000..6738cac --- /dev/null +++ b/include/estd/ReverseAdaptor.hpp @@ -0,0 +1,83 @@ +/** + * \file + * \brief ReverseAdaptor template class header. + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_REVERSEADAPTOR_HPP_ +#define ESTD_REVERSEADAPTOR_HPP_ + +namespace estd +{ + +/** + * \brief ReverseAdaptor template class is an adaptor that "reverses" access to the container + * + * \tparam T is the type of container + */ + +template<typename T> +class ReverseAdaptor +{ +public: + + /** + * \brief ReverseAdaptor's constructor. + * + * \param [in] container is a reference to container + */ + + constexpr explicit ReverseAdaptor(T& container) noexcept : + container_{container} + { + + } + + /** + * \return reverse_iterator to the beginning of "reversed" container (last element of original container) + */ + + typename T::reverse_iterator begin() const noexcept + { + return container_.rbegin(); + } + + /** + * \return reverse_iterator to the end of "reversed" container (before-the-first element of original container) + */ + + typename T::reverse_iterator end() const noexcept + { + return container_.rend(); + } + +private: + + /// reference to container + T& container_; +}; + +/** + * \brief Helper factory function to make ReverseAdaptor object with deduced template arguments + * + * \tparam T is the type of container + * + * \param [in] container is a reference to container + * + * \return ReverseAdaptor object + */ + +template<typename T> +ReverseAdaptor<T> makeReverseAdaptor(T& container) +{ + return ReverseAdaptor<T>{container}; +} + +} // namespace estd + +#endif // ESTD_REVERSEADAPTOR_HPP_ diff --git a/include/estd/SortedIntrusiveForwardList.hpp b/include/estd/SortedIntrusiveForwardList.hpp new file mode 100644 index 0000000..de35845 --- /dev/null +++ b/include/estd/SortedIntrusiveForwardList.hpp @@ -0,0 +1,355 @@ +/** + * \file + * \brief SortedIntrusiveForwardList template class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_SORTEDINTRUSIVEFORWARDLIST_HPP_ +#define ESTD_SORTEDINTRUSIVEFORWARDLIST_HPP_ + +#include "estd/IntrusiveForwardList.hpp" + +#include <algorithm> + +namespace estd +{ + +/** + * \brief SortedIntrusiveForwardList class is an IntrusiveForwardList with sorted elements + * + * This class tries to provide an interface similar to std::forward_list. + * + * \note The elements are sorted as long as the user does not modify the contents. + * + * \tparam Compare is a type of functor used for comparison, std::less results in descending order, std::greater - in + * ascending order + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; using different type than \a T can be used + * to break circular dependencies, because \a T must be fully defined to instantiate this class, but it is enough to + * forward declare \a U - it only needs to be fully defined to use member functions + */ + +template<typename Compare, typename T, IntrusiveForwardListNode T::* NodePointer, typename U = T> +class SortedIntrusiveForwardList +{ +public: + + /// unsorted intrusive forward list used internally + using UnsortedIntrusiveForwardList = IntrusiveForwardList<T, NodePointer, U>; + + /// const iterator of elements on the list + using const_iterator = typename UnsortedIntrusiveForwardList::const_iterator; + + /// const pointer to value linked in the list + using const_pointer = typename UnsortedIntrusiveForwardList::const_pointer; + + /// const reference to value linked in the list + using const_reference = typename UnsortedIntrusiveForwardList::const_reference; + + /// iterator of elements on the list + using iterator = typename UnsortedIntrusiveForwardList::iterator; + + /// pointer to value linked in the list + using pointer = typename UnsortedIntrusiveForwardList::pointer; + + /// reference to value linked in the list + using reference = typename UnsortedIntrusiveForwardList::reference; + + /// value linked in the list + using value_type = typename UnsortedIntrusiveForwardList::value_type; + + /** + * \brief SortedIntrusiveForwardList's constructor + * + * \param [in] compare is a reference to Compare object used to copy-construct internal comparison functor + */ + + constexpr SortedIntrusiveForwardList(const Compare& compare = Compare{}) : + implementation_{compare} + { + + } + + /** + * \return iterator of "one before the first" element on the list + */ + + iterator before_begin() + { + return implementation_.intrusiveForwardList.before_begin(); + } + + /** + * \return const iterator of "one before the first" element on the list + */ + + const_iterator before_begin() const + { + return implementation_.intrusiveForwardList.before_begin(); + } + + /** + * \return iterator of first element on the list + */ + + iterator begin() + { + return implementation_.intrusiveForwardList.begin(); + } + + /** + * \return const iterator of first element on the list + */ + + const_iterator begin() const + { + return implementation_.intrusiveForwardList.begin(); + } + + /** + * \return const iterator of "one before the first" element on the list + */ + + const_iterator cbefore_begin() const + { + return implementation_.intrusiveForwardList.cbefore_begin(); + } + + /** + * \return const iterator of first element on the list + */ + + const_iterator cbegin() const + { + return implementation_.intrusiveForwardList.cbegin(); + } + + /** + * \return const iterator of "one past the last" element on the list + */ + + const_iterator cend() const + { + return implementation_.intrusiveForwardList.cend(); + } + + /** + * \brief Unlinks all elements from the list. + */ + + void clear() + { + implementation_.intrusiveForwardList.clear(); + } + + /** + * \return true is the list is empty, false otherwise + */ + + bool empty() const + { + return implementation_.intrusiveForwardList.empty(); + } + + /** + * \return iterator of "one past the last" element on the list + */ + + iterator end() + { + return implementation_.intrusiveForwardList.end(); + } + + /** + * \return const iterator of "one past the last" element on the list + */ + + const_iterator end() const + { + return implementation_.intrusiveForwardList.end(); + } + + /** + * \return reference to first element on the list + */ + + reference front() + { + return implementation_.intrusiveForwardList.front(); + } + + /** + * \return const reference to first element on the list + */ + + const_reference front() const + { + return implementation_.intrusiveForwardList.front(); + } + + /** + * \brief Links the element in the list, keeping it sorted. + * + * \param [in] newElement is a reference to the element that will be linked in the list + * + * \return iterator of \a newElement + */ + + iterator insert(reference newElement) + { + return UnsortedIntrusiveForwardList::insert_after(implementation_.findInsertPositionBefore(newElement), + newElement); + } + + /** + * \brief Unlinks the first element from the list. + */ + + void pop_front() + { + implementation_.intrusiveForwardList.pop_front(); + } + + /** + * \brief Transfers the element from another list to this one, keeping it sorted. + * + * \param [in] beforeSplicedElement is an iterator of the element preceding the one which will be spliced from + * another list to this one + */ + + void splice_after(const iterator beforeSplicedElement) + { + const auto splicedElement = std::next(beforeSplicedElement); + UnsortedIntrusiveForwardList::splice_after(implementation_.findInsertPositionBefore(*splicedElement), + beforeSplicedElement); + } + + /** + * \brief Swaps contents with another list. + * + * \param [in] other is a reference to SortedIntrusiveForwardList with which contents of this list will be swapped + */ + + void swap(SortedIntrusiveForwardList& other) + { + implementation_.swap(other.implementation_); + } + + /** + * \brief Unlinks the element following \a position from the list. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is an iterator preceding the element which will be unlinked from the list + * + * \return iterator of the element that was following the element which was unlinked + */ + + static iterator erase_after(const iterator position) + { + return UnsortedIntrusiveForwardList::erase_after(position); + } + + SortedIntrusiveForwardList(const SortedIntrusiveForwardList&) = delete; + SortedIntrusiveForwardList(SortedIntrusiveForwardList&&) = default; + const SortedIntrusiveForwardList& operator=(const SortedIntrusiveForwardList&) = delete; + SortedIntrusiveForwardList& operator=(SortedIntrusiveForwardList&&) = delete; + +private: + + /// Implementation struct is used primarily for "Empty Base Optimization" with \a Compare type + struct Implementation : public Compare + { + /** + * \brief Implementation's constructor + * + * \param [in] comparee is a reference to Compare object used to copy-construct internal comparison functor + */ + + constexpr Implementation(const Compare& comparee) : + Compare{comparee}, + intrusiveForwardList{} + { + + } + + /** + * \brief Finds insert position "before" the position that satisfies sorting criteria. + * + * \param [in] newElement is a const reference to new element that is going to be inserted/spliced + * + * \return iterator for which Compare's function call operator of next value and \a newElement returns true. + */ + + iterator findInsertPositionBefore(const_reference newElement) + { + auto it = intrusiveForwardList.before_begin(); + auto next = intrusiveForwardList.begin(); + const auto end = intrusiveForwardList.end(); + + while (next != end && this->Compare::operator()(*next, newElement) == false) + { + it = next; + ++next; + } + + return it; + } + + /** + * \brief Swaps contents with another instance. + * + * \param [in] other is a reference to Implementation with which contents of this instance will be swapped + */ + + void swap(Implementation& other) + { + intrusiveForwardList.swap(other.intrusiveForwardList); + using std::swap; + swap(static_cast<Compare&>(*this), static_cast<Compare&>(other)); + } + + Implementation(const Implementation&) = delete; + Implementation(Implementation&&) = default; + const Implementation& operator=(const Implementation&) = delete; + Implementation& operator=(Implementation&&) = delete; + + /// internal unsorted IntrusiveForwardList + UnsortedIntrusiveForwardList intrusiveForwardList; + }; + + /// internal Implementation object - unsorted IntrusiveForwardList and Compare instance + Implementation implementation_; +}; + +/** + * \brief Swaps contents of two lists. + * + * \tparam Compare is a type of functor used for comparison, std::less results in descending order, std::greater - in + * ascending order + * \tparam T is the type that has the IntrusiveForwardListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveForwardListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a reference to SortedIntrusiveForwardList with which contents of \a right will be swapped + * \param [in] right is a reference to SortedIntrusiveForwardList with which contents of \a left will be swapped + */ + +template<typename Compare, typename T, IntrusiveForwardListNode T::* NodePointer, typename U = T> +inline void swap(SortedIntrusiveForwardList<Compare, T, NodePointer, U>& left, + SortedIntrusiveForwardList<Compare, T, NodePointer, U>& right) +{ + left.swap(right); +} + +} // namespace estd + +#endif // ESTD_SORTEDINTRUSIVEFORWARDLIST_HPP_ diff --git a/include/estd/SortedIntrusiveList.hpp b/include/estd/SortedIntrusiveList.hpp new file mode 100644 index 0000000..1d65ee3 --- /dev/null +++ b/include/estd/SortedIntrusiveList.hpp @@ -0,0 +1,353 @@ +/** + * \file + * \brief SortedIntrusiveList template class header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_SORTEDINTRUSIVELIST_HPP_ +#define ESTD_SORTEDINTRUSIVELIST_HPP_ + +#include "estd/IntrusiveList.hpp" + +#include <algorithm> + +namespace estd +{ + +/** + * \brief SortedIntrusiveList class is an IntrusiveList with sorted elements + * + * This class tries to provide an interface similar to std::list. + * + * \note The elements are sorted as long as the user does not modify the contents. + * + * \tparam Compare is a type of functor used for comparison, std::less results in descending order, std::greater - in + * ascending order + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; using different type than \a T can be used + * to break circular dependencies, because \a T must be fully defined to instantiate this class, but it is enough to + * forward declare \a U - it only needs to be fully defined to use member functions + */ + +template<typename Compare, typename T, IntrusiveListNode T::* NodePointer, typename U = T> +class SortedIntrusiveList +{ +public: + + /// unsorted intrusive list used internally + using UnsortedIntrusiveList = IntrusiveList<T, NodePointer, U>; + + /// const iterator of elements on the list + using const_iterator = typename UnsortedIntrusiveList::const_iterator; + + /// const reverse iterator of elements on the list + using const_reverse_iterator = typename UnsortedIntrusiveList::const_reverse_iterator; + + /// const pointer to value linked in the list + using const_pointer = typename UnsortedIntrusiveList::const_pointer; + + /// const reference to value linked in the list + using const_reference = typename UnsortedIntrusiveList::const_reference; + + /// iterator of elements on the list + using iterator = typename UnsortedIntrusiveList::iterator; + + /// reverse iterator of elements on the list + using reverse_iterator = typename UnsortedIntrusiveList::reverse_iterator; + + /// pointer to value linked in the list + using pointer = typename UnsortedIntrusiveList::pointer; + + /// reference to value linked in the list + using reference = typename UnsortedIntrusiveList::reference; + + /// value linked in the list + using value_type = typename UnsortedIntrusiveList::value_type; + + /** + * \brief SortedIntrusiveList's constructor + * + * \param [in] compare is a reference to Compare object used to copy-construct internal comparison functor + */ + + constexpr SortedIntrusiveList(const Compare& compare = Compare{}) : + implementation_{compare} + { + + } + + /** + * \return reference to last element on the list + */ + + reference back() + { + return implementation_.intrusiveList.back(); + } + + /** + * \return const reference to last element on the list + */ + + const_reference back() const + { + return implementation_.intrusiveList.back(); + } + + /** + * \return iterator of first element on the list + */ + + iterator begin() + { + return implementation_.intrusiveList.begin(); + } + + /** + * \return const iterator of first element on the list + */ + + const_iterator begin() const + { + return implementation_.intrusiveList.begin(); + } + + /** + * \return const iterator of first element on the list + */ + + const_iterator cbegin() const + { + return implementation_.intrusiveList.cbegin(); + } + + /** + * \return const iterator of "one past the last" element on the list + */ + + const_iterator cend() const + { + return implementation_.intrusiveList.cend(); + } + + /** + * \brief Unlinks all elements from the list. + */ + + void clear() + { + implementation_.intrusiveList.clear(); + } + + /** + * \return true is the list is empty, false otherwise + */ + + bool empty() const + { + return implementation_.intrusiveList.empty(); + } + + /** + * \return iterator of "one past the last" element on the list + */ + + iterator end() + { + return implementation_.intrusiveList.end(); + } + + /** + * \return const iterator of "one past the last" element on the list + */ + + const_iterator end() const + { + return implementation_.intrusiveList.end(); + } + + /** + * \return reference to first element on the list + */ + + reference front() + { + return implementation_.intrusiveList.front(); + } + + /** + * \return const reference to first element on the list + */ + + const_reference front() const + { + return implementation_.intrusiveList.front(); + } + + /** + * \brief Links the element in the list, keeping it sorted. + * + * \param [in] newElement is a reference to the element that will be linked in the list + * + * \return iterator of \a newElement + */ + + iterator insert(reference newElement) + { + return UnsortedIntrusiveList::insert(implementation_.findInsertPosition(newElement), newElement); + } + + /** + * \brief Unlinks the last element from the list. + */ + + void pop_back() + { + implementation_.intrusiveList.pop_back(); + } + + /** + * \brief Unlinks the first element from the list. + */ + + void pop_front() + { + implementation_.intrusiveList.pop_front(); + } + + /** + * \brief Transfers the element from another list to this one, keeping it sorted. + * + * \param [in] splicedElement is an iterator of the element that will be spliced from another list to this one + */ + + void splice(const iterator splicedElement) + { + UnsortedIntrusiveList::splice(implementation_.findInsertPosition(*splicedElement), splicedElement); + } + + /** + * \brief Swaps contents with another list. + * + * \param [in] other is a reference to SortedIntrusiveList with which contents of this list will be swapped + */ + + void swap(SortedIntrusiveList& other) + { + implementation_.swap(other.implementation_); + } + + /** + * \brief Unlinks the element at \a position from the list. + * + * \note No instance of the list is needed for this operation. + * + * \param [in] position is an iterator of the element that will be unlinked from the list + * + * \return iterator of the element that was following the element which was unlinked + */ + + static iterator erase(const iterator position) + { + return UnsortedIntrusiveList::erase(position); + } + + SortedIntrusiveList(const SortedIntrusiveList&) = delete; + SortedIntrusiveList(SortedIntrusiveList&&) = default; + const SortedIntrusiveList& operator=(const SortedIntrusiveList&) = delete; + SortedIntrusiveList& operator=(SortedIntrusiveList&&) = delete; + +private: + + /// Implementation struct is used primarily for "Empty Base Optimization" with \a Compare type + struct Implementation : public Compare + { + /** + * \brief Implementation's constructor + * + * \param [in] comparee is a reference to Compare object used to copy-construct internal comparison functor + */ + + constexpr Implementation(const Compare& comparee) : + Compare{comparee}, + intrusiveList{} + { + + } + + /** + * \brief Finds insert position that satisfies sorting criteria. + * + * \param [in] newElement is a const reference to new element that is going to be inserted/spliced + * + * \return iterator for which Compare's function call operator of dereferenced value and \a newElement returns + * true. + */ + + iterator findInsertPosition(const_reference newElement) + { + return std::find_if(intrusiveList.begin(), intrusiveList.end(), + [this, &newElement](const_reference& element) -> bool + { + return this->Compare::operator()(element, newElement); + } + ); + } + + /** + * \brief Swaps contents with another instance. + * + * \param [in] other is a reference to Implementation with which contents of this instance will be swapped + */ + + void swap(Implementation& other) + { + intrusiveList.swap(other.intrusiveList); + using std::swap; + swap(static_cast<Compare&>(*this), static_cast<Compare&>(other)); + } + + Implementation(const Implementation&) = delete; + Implementation(Implementation&&) = default; + const Implementation& operator=(const Implementation&) = delete; + Implementation& operator=(Implementation&&) = delete; + + /// internal unsorted IntrusiveList + UnsortedIntrusiveList intrusiveList; + }; + + /// internal Implementation object - unsorted IntrusiveList and Compare instance + Implementation implementation_; +}; + +/** + * \brief Swaps contents of two lists. + * + * \tparam Compare is a type of functor used for comparison, std::less results in descending order, std::greater - in + * ascending order + * \tparam T is the type that has the IntrusiveListNode variable + * \tparam NodePointer is a pointer-to-member to IntrusiveListNode variable in \a T + * \tparam U is the type that will be stored on the list; it can be different from \a T, but must be implicitly + * convertible to \a T (so usually a type derived from \a T); default - \a T; + * + * \param [in] left is a reference to SortedIntrusiveList with which contents of \a right will be swapped + * \param [in] right is a reference to SortedIntrusiveList with which contents of \a left will be swapped + */ + +template<typename Compare, typename T, IntrusiveListNode T::* NodePointer, typename U = T> +inline void swap(SortedIntrusiveList<Compare, T, NodePointer, U>& left, + SortedIntrusiveList<Compare, T, NodePointer, U>& right) +{ + left.swap(right); +} + +} // namespace estd + +#endif // ESTD_SORTEDINTRUSIVELIST_HPP_ diff --git a/include/estd/TypeErasedFunctor.hpp b/include/estd/TypeErasedFunctor.hpp new file mode 100644 index 0000000..a19d59b --- /dev/null +++ b/include/estd/TypeErasedFunctor.hpp @@ -0,0 +1,99 @@ +/** + * \file + * \brief TypeErasedFunctor template class header + * + * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_TYPEERASEDFUNCTOR_HPP_ +#define ESTD_TYPEERASEDFUNCTOR_HPP_ + +namespace estd +{ + +template<typename Signature, bool NonConst = {}> +class TypeErasedFunctor; + +/** + * \brief TypeErasedFunctor class is an interface for type-erased functors. + * + * Overload with const operator()(). + * + * \tparam R is the type returned by <em>TypeErasedFunctor::operator()() const</em> + * \tparam Args are the types of arguments for <em>TypeErasedFunctor::operator()() const</em> + */ + +template<typename R, typename... Args> +class TypeErasedFunctor<R(Args...), false> +{ +public: + + /** + * \brief Function call operator of TypeErasedFunctor + * + * \param [in,out] args are arguments for derived function + * + * \return value returned by derived function + */ + + virtual R operator()(Args... args) const = 0; + +protected: + + /** + * \brief TypeErasedFunctor's destructor + * + * \note Polymorphic objects of TypeErasedFunctor type must not be deleted via pointer/reference + */ + + ~TypeErasedFunctor() + { + + } +}; + +/** + * \brief TypeErasedFunctor class is an interface for type-erased functors. + * + * Overload with non-const operator()(). + * + * \tparam R is the type returned by <em>TypeErasedFunctor::operator()()</em> + * \tparam Args are the types of arguments for <em>TypeErasedFunctor::operator()()</em> + */ + +template<typename R, typename... Args> +class TypeErasedFunctor<R(Args...), true> +{ +public: + + /** + * \brief Function call operator of TypeErasedFunctor + * + * \param [in,out] args are arguments for derived function + * + * \return value returned by derived function + */ + + virtual R operator()(Args... args) = 0; + +protected: + + /** + * \brief TypeErasedFunctor's destructor + * + * \note Polymorphic objects of TypeErasedFunctor type must not be deleted via pointer/reference + */ + + ~TypeErasedFunctor() + { + + } +}; + +} // namespace estd + +#endif // ESTD_TYPEERASEDFUNCTOR_HPP_ diff --git a/include/estd/apply.hpp b/include/estd/apply.hpp new file mode 100644 index 0000000..743cce6 --- /dev/null +++ b/include/estd/apply.hpp @@ -0,0 +1,73 @@ +/** + * \file + * \brief apply() header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_APPLY_HPP_ +#define ESTD_APPLY_HPP_ + +#include "estd/IntegerSequence.hpp" +#include "estd/invoke.hpp" + +#include <tuple> + +namespace estd +{ + +namespace internal +{ + +/** + * \brief Implementation of apply() + * + * \tparam Function is the function object that will be invoked + * \tparam Tuple is the type of tuple of arguments + * \tparam Indexes is a sequence of std::size_t indexes for \a Tuple + * + * \param [in] function is the function object that will be executed + * \param [in] tuple is the tuple of arguments + * + * \return value returned by call to \a function with arguments from \a tuple + */ + +template<typename Function, typename Tuple, std::size_t... Indexes> +constexpr auto apply(Function&& function, Tuple&& tuple, estd::IndexSequence<Indexes...>) -> + decltype(estd::invoke(std::forward<Function>(function), std::get<Indexes>(std::forward<Tuple>(tuple))...)) +{ + return estd::invoke(std::forward<Function>(function), std::get<Indexes>(std::forward<Tuple>(tuple))...); +} + +} // namespace internal + +/** + * \brief Invokes callable object with a tuple of arguments. + * + * Implementation inspired by http://en.cppreference.com/w/cpp/experimental/apply + * + * \tparam Function is the function object that will be invoked + * \tparam Tuple is the type of tuple of arguments + * + * \param [in] function is the function object that will be executed + * \param [in] tuple is the tuple of arguments + * + * \return value returned by call to \a function with arguments from \a tuple + */ + +template <typename Function, typename Tuple> +constexpr auto apply(Function&& function, Tuple&& tuple) -> + decltype(internal::apply(std::forward<Function>(function), std::forward<Tuple>(tuple), + estd::MakeIndexSequence<std::tuple_size<typename std::decay<Tuple>::type>{}>{})) +{ + return internal::apply(std::forward<Function>(function), std::forward<Tuple>(tuple), + estd::MakeIndexSequence<std::tuple_size<typename std::decay<Tuple>::type>{}>{}); +} + +} // namespace estd + +#endif // ESTD_APPLY_HPP_ diff --git a/include/estd/invoke.hpp b/include/estd/invoke.hpp new file mode 100644 index 0000000..bdac1a0 --- /dev/null +++ b/include/estd/invoke.hpp @@ -0,0 +1,147 @@ +/** + * \file + * \brief invoke() header + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef ESTD_INVOKE_HPP_ +#define ESTD_INVOKE_HPP_ + +#include <utility> + +namespace estd +{ + +namespace internal +{ + +/** + * \brief Implementation of invoke() for function objects + * + * \tparam Function is the function object that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] function is the function object that will be executed + * \param [in] args are arguments for \a function + * + * \return value returned by call to \a function with \a args + */ + +template<typename Function, typename... Args> +inline auto invoke(Function&& function, Args&&... args) -> + decltype(std::forward<Function>(function)(std::forward<Args>(args)...)) +{ + return std::forward<Function>(function)(std::forward<Args>(args)...); +} + +/** + * \brief Implementation of invoke() for member functions + * + * \tparam T is the type returned by call to member function + * \tparam Base is the type of base class + * \tparam Derived is the type of derived class + * \tparam Args are the arguments for member function + * + * \param [in] memberFunction is a pointer to member function of \a object that will be executed + * \param [in] object is an object or a reference to object on which \a memberFunction will be executed + * \param [in] args are arguments for \a memberFunction + * + * \return value returned by call to \a memberFunction on \a object with \a args + */ + +template<typename T, typename Base, typename Derived, typename... Args> +inline auto invoke(T Base::* memberFunction, Derived&& object, Args&&... args) -> + decltype((std::forward<Derived>(object).*memberFunction)(std::forward<Args>(args)...)) +{ + return (std::forward<Derived>(object).*memberFunction)(std::forward<Args>(args)...); +} + +/** + * \brief Implementation of invoke() for member functions + * + * \tparam MemberFunction is the type or pointer to member function + * \tparam Pointer is the type of pointer to object + * \tparam Args are the arguments for member function + * + * \param [in] memberFunction is a pointer to member function of object that will be executed + * \param [in] pointer is a pointer to object on which \a memberFunction will be executed + * \param [in] args are arguments for \a memberFunction + * + * \return value returned by call to \a memberFunction on \a (*pointer) with \a args + */ + +template<typename MemberFunction, typename Pointer, typename... Args> +inline auto invoke(MemberFunction memberFunction, Pointer&& pointer, Args&&... args) -> + decltype(((*std::forward<Pointer>(pointer)).*memberFunction)(std::forward<Args>(args)...)) +{ + return ((*std::forward<Pointer>(pointer)).*memberFunction)(std::forward<Args>(args)...); +} + +/** + * \brief Implementation of invoke() for data members + * + * \tparam T is the type of data member + * \tparam Base is the type of base class + * \tparam Derived is the type of derived class + * + * \param [in] dataMember is a pointer to data member of \a object that will be accessed + * \param [in] object is an object or a reference to object in which \a dataMember will be accessed + * + * \return value returned by access to \a dataMember in \a object + */ + +template<typename T, typename Base, typename Derived> +inline auto invoke(T Base::* dataMember, Derived&& object) -> decltype(std::forward<Derived>(object).*dataMember) +{ + return std::forward<Derived>(object).*dataMember; +} + +/** + * \brief Implementation of invoke() for data members + * + * \tparam DataMember is the type or pointer to data member + * \tparam Pointer is the type of pointer to object + * + * \param [in] dataMember is a pointer to data member of object that will be accessed + * \param [in] pointer is a pointer to object in which \a dataMember will be accessed + * + * \return value returned by access to \a dataMember in \a (*pointer) + */ + +template<typename DataMember, typename Pointer> +inline auto invoke(DataMember dataMember, Pointer&& pointer) -> decltype((*std::forward<Pointer>(pointer)).*dataMember) +{ + return (*std::forward<Pointer>(pointer)).*dataMember; +} + +} // namespace internal + +/** + * \brief Invokes callable object in an appropriate way. + * + * Implementation inspired by http://en.cppreference.com/w/cpp/utility/functional/invoke + * + * \tparam Function is the function object that will be executed + * \tparam Args are the arguments for \a Function + * + * \param [in] function is the function object that will be executed + * \param [in] args are arguments for \a function + * + * \return value returned by call to \a function with \a args + */ + +template<typename Function, typename... Args> +auto invoke(Function&& function, Args&&... args) -> + decltype(internal::invoke(std::forward<Function>(function), std::forward<Args>(args)...)) +{ + return internal::invoke(std::forward<Function>(function), std::forward<Args>(args)...); +} + +} // namespace estd + +#endif // ESTD_INVOKE_HPP_ diff --git a/include/motor.h b/include/motor.h deleted file mode 100644 index cf15088..0000000 --- a/include/motor.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef MOTOR_H_ -#define MOTOR_H_ - -#include "ch.h" -#include "hal.h" -#include "vex.h" - -typedef enum { - /* 1 */ mClawThingy = kVexMotor_1, - /* 2 */ mLiftHighLeft, - /* 3 */ mLiftLowLeft, - /* 4 */ mLiftHighRight, - /* 5 */ mLiftLowRight, - /* 6 */ mDriveFrontLeft, - /* 7 */ mDriveBackLeft, - /* 8 */ mDriveBackRight, - /* 9 */ mDriveFrontRight, - /* 10 */ mPickupThingy, -} motor_port_t; - -constexpr const tVexImeChannels iLiftLowLeft = kImeChannel_1; -constexpr const tVexImeChannels iLiftHighLeft = kImeChannel_2; -constexpr const tVexImeChannels iLiftLowRight = kImeChannel_3; -constexpr const tVexImeChannels iLiftHighRight = kImeChannel_4; - -#define DIGI_CFG_DIGI_OUT(p) { kVexDigital_##p, kVexSensorDigitalOutput, kVexConfigOutput, 0 } -#define DIGI_CFG_DIGI_IN(p) { kVexDigital_##p, kVexSensorDigitalInput, kVexConfigInput, 0 } - -#define MOTOR_CFG_MOT(p, t, r) (tVexMotor)p, kVexMotor##t, kVexMotor##r -#define MOTOR_CFG_NOIME kVexSensorNone, 0 -#define MOTOR_CFG_IME(c) kVexSensorIME, c - -#endif // MOTOR_H_ diff --git a/include/sys/signal.h b/include/sys/signal.h new file mode 100644 index 0000000..02509ac --- /dev/null +++ b/include/sys/signal.h @@ -0,0 +1,41 @@ +/** + * \file + * \brief Override for newlib's sys/signal.h + * + * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info + * + * \par License + * 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/. + */ + +#ifndef INCLUDE_SYS_SIGNAL_H_ +#define INCLUDE_SYS_SIGNAL_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* def __cplusplus */ + +/*---------------------------------------------------------------------------------------------------------------------+ +| global types ++---------------------------------------------------------------------------------------------------------------------*/ + +/** pointer to function taking an int argument (required by newlib's signal.h) */ +typedef void (* _sig_func_ptr)(int); + +/** standard sigval union - integer and void pointer */ +union sigval +{ + /** integer signal value */ + int sival_int; + + /** pointer signal value */ + void* sival_ptr; +}; + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* def __cplusplus */ + +#endif /* INCLUDE_SYS_SIGNAL_H_ */ diff --git a/libdistortos.a b/libdistortos.a Binary files differnew file mode 100644 index 0000000..54a0aa1 --- /dev/null +++ b/libdistortos.a diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 38b6a1b..0000000 --- a/main.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/*-----------------------------------------------------------------------------*/
-/* */
-/* Copyright (c) James Pearman */
-/* 2013 */
-/* All Rights Reserved */
-/* */
-/*-----------------------------------------------------------------------------*/
-/* */
-/* Module: vexmain.c */
-/* Author: James Pearman */
-/* Created: 7 May 2013 */
-/* */
-/* Revisions: */
-/* V1.00 04 July 2013 - Initial release */
-/* */
-/*-----------------------------------------------------------------------------*/
-/* */
-/* The author is supplying this software for use with the VEX cortex */
-/* control system. This file can be freely distributed and teams are */
-/* authorized to freely use this program , however, it is requested that */
-/* improvements or additions be shared with the Vex community via the vex */
-/* forum. Please acknowledge the work of the authors when appropriate. */
-/* Thanks. */
-/* */
-/* 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. */
-/* */
-/* The author can be contacted on the vex forums as jpearman */
-/* or electronic mail using jbpearman_at_mac_dot_com */
-/* Mentor for team 8888 RoboLancers, Pasadena CA. */
-/* */
-/*-----------------------------------------------------------------------------*/
-
-
-#include <string.h>
-
-#include "ch.h"
-#include "hal.h"
-#include "chprintf.h"
-#include "vex.h"
-
-/*-----------------------------------------------------------------------------*/
-/* Command line related. */
-/*-----------------------------------------------------------------------------*/
-
-#define SHELL_WA_SIZE THD_WA_SIZE(512)
-
-// Shell command
-static const ShellCommand commands[] = {
- {"adc", vexAdcDebug },
- {"spi", vexSpiDebug },
- {"motor", vexMotorDebug},
- {"lcd", vexLcdDebug},
- {"enc", vexEncoderDebug},
- {"son", vexSonarDebug},
- {"ime", vexIMEDebug},
- {"test", vexTestDebug},
- {NULL, NULL}
-};
-
-// configuration for the shell
-static const ShellConfig shell_cfg1 = {
- (vexStream *)SD_CONSOLE,
- commands
-};
-
-/*-----------------------------------------------------------------------------*/
-// Application entry point. */
-/*-----------------------------------------------------------------------------*/
-
-int main(void)
-{
- Thread *shelltp = NULL;
- short timeout = 0;
-
- // System initializations.
- // - HAL initialization, this also initializes the configured device drivers
- // and performs the board-specific initializations.
- // - Kernel initialization, the main() function becomes a thread and the
- // RTOS is active.
- halInit();
- chSysInit();
-
- // Init the serial port associated with the console
- vexConsoleInit();
-
- // init VEX
- vexCortexInit();
-
- // wait for good spi comms
- while( vexSpiGetOnlineStatus() == 0 )
- {
- // wait for a while
- chThdSleepMilliseconds(100);
- // dump after 5 seconds
- if(timeout++ == 50)
- break;
- }
-
- // Shell manager initialization.
- shellInit();
-
- // spin in loop monitoring the shell
- while (TRUE)
- {
- if (!shelltp)
- shelltp = shellCreate(&shell_cfg1, SHELL_WA_SIZE, NORMALPRIO);
- else
- if (chThdTerminated(shelltp))
- {
- chThdRelease(shelltp); /* Recovers memory of the previous shell. */
- shelltp = NULL; /* Triggers spawning of a new shell. */
- }
-
- chThdSleepMilliseconds(50);
- }
-}
diff --git a/mcuconf.h b/mcuconf.h deleted file mode 100644 index 8095cb7..0000000 --- a/mcuconf.h +++ /dev/null @@ -1,204 +0,0 @@ -/*
- ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
-
- 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.
-*/
-
-#define STM32F103_MCUCONF
-
-/*
- * STM32F1xx drivers configuration.
- * The following settings override the default settings present in
- * the various device driver implementation headers.
- * Note that the settings for each driver only have effect if the whole
- * driver is enabled in halconf.h.
- *
- * IRQ priorities:
- * 15...0 Lowest...Highest.
- *
- * DMA priorities:
- * 0...3 Lowest...Highest.
- */
-
-/*
- * HAL driver system settings.
- */
-#define STM32_NO_INIT FALSE
-#define STM32_HSI_ENABLED TRUE
-#define STM32_LSI_ENABLED FALSE
-#define STM32_HSE_ENABLED TRUE
-#define STM32_LSE_ENABLED FALSE
-#define STM32_SW STM32_SW_PLL
-#define STM32_PLLSRC STM32_PLLSRC_HSE
-#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1
-#define STM32_PLLMUL_VALUE 9
-#define STM32_HPRE STM32_HPRE_DIV1
-#define STM32_PPRE1 STM32_PPRE1_DIV2
-#define STM32_PPRE2 STM32_PPRE2_DIV1
-#define STM32_ADCPRE STM32_ADCPRE_DIV8
-#define STM32_USB_CLOCK_REQUIRED TRUE
-#define STM32_USBPRE STM32_USBPRE_DIV1P5
-#define STM32_MCOSEL STM32_MCOSEL_NOCLOCK
-#define STM32_RTCSEL STM32_RTCSEL_HSEDIV
-#define STM32_PVD_ENABLE FALSE
-#define STM32_PLS STM32_PLS_LEV0
-
-/*
- * ADC driver system settings.
- */
-#define STM32_ADC_USE_ADC1 TRUE
-#define STM32_ADC_ADC1_DMA_PRIORITY 2
-#define STM32_ADC_ADC1_IRQ_PRIORITY 5
-
-/*
- * CAN driver system settings.
- */
-#define STM32_CAN_USE_CAN1 TRUE
-#define STM32_CAN_CAN1_IRQ_PRIORITY 11
-
-/*
- * EXT driver system settings.
- */
-#define STM32_EXT_EXTI0_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI1_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI2_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI3_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI4_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI5_9_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI10_15_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI16_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI17_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI18_IRQ_PRIORITY 6
-#define STM32_EXT_EXTI19_IRQ_PRIORITY 6
-
-/*
- * GPT driver system settings.
- */
-#define STM32_GPT_USE_TIM1 TRUE
-#define STM32_GPT_USE_TIM2 TRUE
-#define STM32_GPT_USE_TIM3 FALSE
-#define STM32_GPT_USE_TIM4 FALSE
-#define STM32_GPT_USE_TIM5 TRUE
-#define STM32_GPT_USE_TIM8 FALSE
-#define STM32_GPT_TIM1_IRQ_PRIORITY 7
-#define STM32_GPT_TIM2_IRQ_PRIORITY 7
-#define STM32_GPT_TIM3_IRQ_PRIORITY 7
-#define STM32_GPT_TIM4_IRQ_PRIORITY 7
-#define STM32_GPT_TIM5_IRQ_PRIORITY 7
-#define STM32_GPT_TIM8_IRQ_PRIORITY 7
-
-/*
- * I2C driver system settings.
- */
-#define STM32_I2C_USE_I2C1 TRUE
-#define STM32_I2C_USE_I2C2 FALSE
-#define STM32_I2C_USE_I2C3 FALSE
-#define STM32_I2C_I2C1_IRQ_PRIORITY 10
-#define STM32_I2C_I2C2_IRQ_PRIORITY 10
-#define STM32_I2C_I2C3_IRQ_PRIORITY 10
-#define STM32_I2C_I2C1_DMA_PRIORITY 1
-#define STM32_I2C_I2C2_DMA_PRIORITY 1
-#define STM32_I2C_I2C3_DMA_PRIORITY 1
-#define STM32_I2C_I2C1_DMA_ERROR_HOOK() chSysHalt()
-#define STM32_I2C_I2C2_DMA_ERROR_HOOK() chSysHalt()
-#define STM32_I2C_I2C3_DMA_ERROR_HOOK() chSysHalt()
-
-/*
- * ICU driver system settings.
- */
-#define STM32_ICU_USE_TIM1 FALSE
-#define STM32_ICU_USE_TIM2 FALSE
-#define STM32_ICU_USE_TIM3 FALSE
-#define STM32_ICU_USE_TIM4 FALSE
-#define STM32_ICU_USE_TIM5 FALSE
-#define STM32_ICU_USE_TIM8 FALSE
-#define STM32_ICU_TIM1_IRQ_PRIORITY 7
-#define STM32_ICU_TIM2_IRQ_PRIORITY 7
-#define STM32_ICU_TIM3_IRQ_PRIORITY 7
-#define STM32_ICU_TIM4_IRQ_PRIORITY 7
-#define STM32_ICU_TIM5_IRQ_PRIORITY 7
-#define STM32_ICU_TIM8_IRQ_PRIORITY 7
-
-/*
- * PWM driver system settings.
- */
-#define STM32_PWM_USE_ADVANCED FALSE
-#define STM32_PWM_USE_TIM1 FALSE
-#define STM32_PWM_USE_TIM2 FALSE
-#define STM32_PWM_USE_TIM3 FALSE
-#define STM32_PWM_USE_TIM4 FALSE
-#define STM32_PWM_USE_TIM5 FALSE
-#define STM32_PWM_USE_TIM8 FALSE
-#define STM32_PWM_TIM1_IRQ_PRIORITY 7
-#define STM32_PWM_TIM2_IRQ_PRIORITY 7
-#define STM32_PWM_TIM3_IRQ_PRIORITY 7
-#define STM32_PWM_TIM4_IRQ_PRIORITY 7
-#define STM32_PWM_TIM5_IRQ_PRIORITY 7
-#define STM32_PWM_TIM8_IRQ_PRIORITY 7
-
-/*
- * RTC driver system settings.
- */
-#define STM32_RTC_IRQ_PRIORITY 15
-
-/*
- * SERIAL driver system settings.
- */
-#define STM32_SERIAL_USE_USART1 TRUE
-#define STM32_SERIAL_USE_USART2 TRUE
-#define STM32_SERIAL_USE_USART3 TRUE
-#define STM32_SERIAL_USE_UART4 FALSE
-#define STM32_SERIAL_USE_UART5 FALSE
-#define STM32_SERIAL_USE_USART6 FALSE
-#define STM32_SERIAL_USART1_PRIORITY 12
-#define STM32_SERIAL_USART2_PRIORITY 12
-#define STM32_SERIAL_USART3_PRIORITY 12
-#define STM32_SERIAL_UART4_PRIORITY 12
-#define STM32_SERIAL_UART5_PRIORITY 12
-#define STM32_SERIAL_USART6_PRIORITY 12
-
-/*
- * SPI driver system settings.
- */
-#define STM32_SPI_USE_SPI1 TRUE
-#define STM32_SPI_USE_SPI2 FALSE
-#define STM32_SPI_USE_SPI3 FALSE
-#define STM32_SPI_SPI1_DMA_PRIORITY 1
-#define STM32_SPI_SPI2_DMA_PRIORITY 1
-#define STM32_SPI_SPI3_DMA_PRIORITY 1
-#define STM32_SPI_SPI1_IRQ_PRIORITY 10
-#define STM32_SPI_SPI2_IRQ_PRIORITY 10
-#define STM32_SPI_SPI3_IRQ_PRIORITY 10
-#define STM32_SPI_DMA_ERROR_HOOK(spip) chSysHalt()
-
-/*
- * UART driver system settings.
- */
-#define STM32_UART_USE_USART1 FALSE
-#define STM32_UART_USE_USART2 FALSE
-#define STM32_UART_USE_USART3 FALSE
-#define STM32_UART_USART1_IRQ_PRIORITY 12
-#define STM32_UART_USART2_IRQ_PRIORITY 12
-#define STM32_UART_USART3_IRQ_PRIORITY 12
-#define STM32_UART_USART1_DMA_PRIORITY 0
-#define STM32_UART_USART2_DMA_PRIORITY 0
-#define STM32_UART_USART3_DMA_PRIORITY 0
-#define STM32_UART_DMA_ERROR_HOOK(uartp) chSysHalt()
-
-/*
- * USB driver system settings.
- */
-#define STM32_USB_USE_USB1 FALSE
-#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE
-#define STM32_USB_USB1_HP_IRQ_PRIORITY 6
-#define STM32_USB_USB1_LP_IRQ_PRIORITY 14
diff --git a/setup.mk b/setup.mk deleted file mode 100644 index 131405e..0000000 --- a/setup.mk +++ /dev/null @@ -1,15 +0,0 @@ -# uncomment these if running from default project location
-# Path to ChibiOS
-#CHIBIOS = ../ChibiOS_2.6.2
-# Path to convex
-#CONVEX = ../convex/cortex
-
-# uncomment to use the optional code like the smart motor library
-#CONVEX_OPT = yes
-
-# User C code files
-VEXUSERSRC = vexuser.cpp src/motor.cpp
-
-# Uncomment and add/modify user include files
-VEXUSERINC = include/
-
diff --git a/src/motor.cpp b/src/motor.cpp deleted file mode 100644 index d9e7c4b..0000000 --- a/src/motor.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include <motor.h> - - diff --git a/vexuser.cpp b/vexuser.cpp deleted file mode 100644 index 8bd3088..0000000 --- a/vexuser.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include <unistd.h>
-#include <array>
-
-#include "ch.h" // needs for all ChibiOS programs
-#include "hal.h" // hardware abstraction layer header
-#include "vex.h" // vex library header
-
-#include <motor.h>
-#include <control.h>
-
-//static WORKING_AREA(waVexIME, 512);
-//static msg_t vexIME(void *);
-
-// Digi IO configuration
-static vexDigiCfg dConfig[] = {
-};
-
-static vexMotorCfg mConfig[] = {
- { MOTOR_CFG_MOT(mClawThingy, 393T, Normal), MOTOR_CFG_NOIME },
- { MOTOR_CFG_MOT(mDriveFrontLeft, 393T, Normal), MOTOR_CFG_NOIME },
- { MOTOR_CFG_MOT(mDriveFrontRight, 393T, Reversed), MOTOR_CFG_NOIME },
- { MOTOR_CFG_MOT(mDriveBackLeft, 393T, Reversed), MOTOR_CFG_NOIME },
- { MOTOR_CFG_MOT(mDriveBackRight, 393T, Normal), MOTOR_CFG_NOIME },
- { MOTOR_CFG_MOT(mLiftLowRight, 393T, Normal), MOTOR_CFG_IME(iLiftLowRight) },
- { MOTOR_CFG_MOT(mLiftHighRight, 393T, Normal), MOTOR_CFG_IME(iLiftHighRight) },
- { MOTOR_CFG_MOT(mLiftLowLeft, 393T, Normal), MOTOR_CFG_IME(iLiftLowLeft) },
- { MOTOR_CFG_MOT(mLiftHighLeft, 393T, Normal), MOTOR_CFG_IME(iLiftHighLeft) },
- { MOTOR_CFG_MOT(mPickupThingy, 393T, Normal), MOTOR_CFG_NOIME }
-};
-
-void vexUserSetup(void)
-{
- vexDigitalConfigure(dConfig, DIG_CONFIG_SIZE(dConfig));
- vexMotorConfigure(mConfig, MOT_CONFIG_SIZE(mConfig));
-}
-
-void vexUserInit(void)
-{}
-
-#define AIRCR_ADDR 0xE000ED0C
-#define VECTKEY 0x05FA
-#define SYSRESETREQ (1<<2)
-#define VECTRESET (1<<0)
-
-void softwareReset(void){
- uint32_t AIRCR = *((uint32_t *)AIRCR_ADDR);
- AIRCR = (AIRCR & 0xFFFF) | (VECTKEY << 16) | SYSRESETREQ | VECTRESET;
- *((volatile uint32_t *)0xE000ED0C) = AIRCR;
- asm("DSB");
- while(1);
-}
-
-msg_t vexAutonomous(void* arg)
-{
- (void)arg;
-
- vexTaskRegister("auton");
-
- vexMotorSet(mClawThingy, -127);
- vexMotorSet(mPickupThingy, -64);
- vexSleep(300);
- vexMotorSet(mClawThingy, 0);
- vexMotorSet(mPickupThingy, 0);
-
- vexMotorSet(mDriveFrontLeft, -127);
- vexMotorSet(mDriveFrontRight, -127);
- vexMotorSet(mDriveBackLeft, -127);
- vexMotorSet(mDriveBackRight, -127);
- vexSleep(3000);
- vexMotorSet(mDriveFrontLeft, 30);
- vexMotorSet(mDriveFrontRight, 30);
- vexMotorSet(mDriveBackLeft, 30);
- vexMotorSet(mDriveBackRight, 30);
- vexSleep(1000);
- vexMotorSet(mDriveFrontLeft, 0);
- vexMotorSet(mDriveFrontRight, 0);
- vexMotorSet(mDriveBackLeft, 0);
- vexMotorSet(mDriveBackRight, 0);
-
- while(1)
- vexSleep(25);
-
- return (msg_t)0;
-}
-
-int doubleButton(int btnp, int btnn, int speed);
-
-msg_t vexOperator(void* arg)
-{
- Controller joyMain (1);
-
- (void)arg;
-
- vexTaskRegister("operator");
-
- //chThdCreateStatic(waVexIME, sizeof(waVexIME), NORMALPRIO - 1, vexIME, nullptr);
-
- while(!chThdShouldTerminate()) {
-
- // control update
- joyMain.update();
-
- // drive motors
- int dx = joyMain->Ch4, dy = -joyMain->Ch3;
- vexMotorSet(mDriveFrontLeft, dy - dx);
- vexMotorSet(mDriveFrontRight, dy + dx);
- vexMotorSet(mDriveBackLeft, dy - dx);
- vexMotorSet(mDriveBackRight, dy + dx);
-
- // lift motors
-#ifndef NEW_LIFT
- int ly = joyMain->Ch2;
- vexMotorSet(mLiftLowRight, ly);
- vexMotorSet(mLiftHighRight, ly);
- vexMotorSet(mLiftLowLeft, ly);
- vexMotorSet(mLiftHighLeft, ly);
-#else
- if (joyMain->Btn8U)
- motorCountInc();
- else if (joyMain->Btn7D)
- motorCountDec();
-#endif // NEW_LIFT
-
- // lift thingy
- vexMotorSet(mPickupThingy, doubleButton(joyMain->Btn5U, joyMain->Btn5D, 64));
-
- // claw thingy
- vexMotorSet(mClawThingy, doubleButton(joyMain->Btn6U, joyMain->Btn6D, 127));
-
- if (joyMain->Btn8R)
- softwareReset();
-
- vexSleep(25);
- }
-
- return (msg_t)0;
-}
-
-
-int doubleButton(int btnp, int btnn, int speed)
-{
- return (btnp ? speed : (btnn ? -speed : 0));
-}
-
-extern "C" {
- void _exit(int code) {
- (void)code;
-
- vexLcdPrintf(0, 0, "PANIC: exit(%d)", code);
- vexLcdPrintf(0, 1, "Halting...");
-
- while(1);
- }
- void _kill(pid_t pid) {
- (void)pid;
- // no way to kill here
- }
- pid_t _getpid(void) {
- // no pids here
- return 0;
- }
-}
-
-/*using CountTuple = std::tuple<tVexMotor, tVexImeChannels, int32_t>;
-
-static std::array<CountTuple, 4> MotorCounts = {
- std::make_tuple(mLiftLowLeft, iLiftLowLeft, 0),
- std::make_tuple(mLiftHighLeft, iLiftHighLeft, 0),
- std::make_tuple(mLiftLowRight, iLiftLowRight, 0),
- std::make_tuple(mLiftHighRight, iLiftHighRight, 0)
-};
-
-void
-motorCountInc(void)
-{
- for (auto &c : MotorCounts)
- std::get<2>(c) += 10;
-}
-
-void
-motorCountDec(void)
-{
- for (auto &c : MotorCounts)
- std::get<2>(c) -= 10;
-}
-
-static msg_t
-vexIME(void *arg)
-{
- (void)arg;
-
- vexTaskRegister("uime");
-
- while (1) {
- for (auto &c : MotorCounts) {
- auto count = vexImeGetPtr(std::get<1>(c))->count;
- auto comp = std::get<2>(c);
-
- if (count > comp)
- vexMotorSet(vexMotorGet(std::get<0>(c)) - 2);
- else if(count < comp)
- vexMotorSet(vexMotorGet(std::get<0>(c)) + 2);
- }
-
- vexSleep(100);
- }
-}*/
|