From 92235fb259b0ebdfc99859c2c95fe1f8c163f411 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Tue, 8 Nov 2016 20:09:05 -0500 Subject: [PATCH] trying out distortos --- Makefile | 239 ---- STM32F103VD.ld | 267 ++++ chconf.h | 531 -------- halconf.h | 345 ----- include/control.h | 71 - include/distortos/ConditionVariable.hpp | 304 +++++ include/distortos/DynamicFifoQueue.hpp | 56 + include/distortos/DynamicMessageQueue.hpp | 60 + include/distortos/DynamicRawFifoQueue.hpp | 42 + include/distortos/DynamicRawMessageQueue.hpp | 42 + include/distortos/DynamicSignalsReceiver.hpp | 52 + include/distortos/DynamicThread.hpp | 373 +++++ include/distortos/DynamicThreadParameters.hpp | 99 ++ include/distortos/FifoQueue.hpp | 682 ++++++++++ include/distortos/MessageQueue.hpp | 762 +++++++++++ include/distortos/Mutex.hpp | 280 ++++ include/distortos/OnceFlag.hpp | 60 + include/distortos/RawFifoQueue.hpp | 463 +++++++ include/distortos/RawMessageQueue.hpp | 555 ++++++++ include/distortos/SchedulingPolicy.hpp | 36 + include/distortos/Semaphore.hpp | 245 ++++ include/distortos/SignalAction.hpp | 96 ++ include/distortos/SignalInformation.hpp | 100 ++ .../SignalInformationQueueWrapper.hpp | 62 + include/distortos/SignalSet.hpp | 179 +++ include/distortos/SignalsCatcher.hpp | 62 + include/distortos/SignalsReceiver.hpp | 58 + include/distortos/SoftwareTimer.hpp | 130 ++ include/distortos/SoftwareTimerCommon.hpp | 87 ++ include/distortos/StaticFifoQueue.hpp | 57 + include/distortos/StaticMessageQueue.hpp | 64 + include/distortos/StaticRawFifoQueue.hpp | 69 + include/distortos/StaticRawMessageQueue.hpp | 71 + include/distortos/StaticSignalsReceiver.hpp | 166 +++ include/distortos/StaticSoftwareTimer.hpp | 90 ++ include/distortos/StaticThread.hpp | 357 +++++ include/distortos/ThisThread-Signals.hpp | 279 ++++ include/distortos/ThisThread.hpp | 158 +++ include/distortos/Thread.hpp | 194 +++ include/distortos/ThreadCommon.hpp | 230 ++++ include/distortos/ThreadState.hpp | 54 + include/distortos/TickClock.hpp | 56 + include/distortos/UndetachableThread.hpp | 57 + .../architecture/InterruptMaskingLock.hpp | 34 + .../InterruptMaskingUnmaskingLock.hpp | 73 + .../architecture/InterruptUnmaskingLock.hpp | 34 + include/distortos/architecture/Stack.hpp | 122 ++ .../architecture/disableInterruptMasking.hpp | 37 + .../architecture/enableInterruptMasking.hpp | 40 + .../distortos/architecture/getMainStack.hpp | 37 + .../architecture/initializeStack.hpp | 50 + .../architecture/lowLevelInitialization.hpp | 34 + .../architecture/requestContextSwitch.hpp | 35 + .../architecture/requestFunctionExecution.hpp | 53 + .../architecture/restoreInterruptMasking.hpp | 39 + .../architecture/startScheduling.hpp | 33 + .../board/lowLevelInitialization.hpp | 36 + include/distortos/callOnce.hpp | 50 + .../distortos/chip/lowLevelInitialization.hpp | 34 + include/distortos/devices/io/InputPin.hpp | 48 + include/distortos/devices/io/OutputPin.hpp | 46 + include/distortos/distortosVersion.h | 37 + .../internal/memory/DeferredThreadDeleter.hpp | 121 ++ .../internal/memory/dummyDeleter.hpp | 48 + .../memory/getDeferredThreadDeleter.hpp | 39 + .../internal/memory/getMallocMutex.hpp | 33 + .../internal/memory/storageDeleter.hpp | 44 + .../internal/scheduler/DynamicThreadBase.hpp | 231 ++++ .../internal/scheduler/MainThread.hpp | 46 + .../internal/scheduler/RoundRobinQuantum.hpp | 123 ++ .../internal/scheduler/Scheduler.hpp | 351 +++++ .../scheduler/SoftwareTimerControlBlock.hpp | 111 ++ .../internal/scheduler/SoftwareTimerList.hpp | 62 + .../scheduler/SoftwareTimerListNode.hpp | 82 ++ .../scheduler/SoftwareTimerSupervisor.hpp | 66 + .../internal/scheduler/ThreadControlBlock.hpp | 336 +++++ .../scheduler/ThreadGroupControlBlock.hpp | 61 + .../internal/scheduler/ThreadList.hpp | 65 + .../internal/scheduler/ThreadListNode.hpp | 86 ++ .../internal/scheduler/forceContextSwitch.hpp | 33 + .../internal/scheduler/getScheduler.hpp | 33 + .../internal/scheduler/idleThreadFunction.hpp | 31 + .../scheduler/lowLevelInitialization.hpp | 44 + .../internal/scheduler/threadRunner.hpp | 48 + .../synchronization/BoundQueueFunctor.hpp | 89 ++ .../synchronization/CallOnceControlBlock.hpp | 172 +++ .../CopyConstructQueueFunctor.hpp | 67 + .../synchronization/FifoQueueBase.hpp | 145 ++ .../synchronization/MemcpyPopQueueFunctor.hpp | 65 + .../MemcpyPushQueueFunctor.hpp | 65 + .../synchronization/MessageQueueBase.hpp | 221 +++ .../MoveConstructQueueFunctor.hpp | 69 + .../synchronization/MutexControlBlock.hpp | 184 +++ .../internal/synchronization/MutexList.hpp | 32 + .../synchronization/MutexListNode.hpp | 53 + .../internal/synchronization/QueueFunctor.hpp | 40 + .../synchronization/SemaphoreFunctor.hpp | 43 + .../SemaphoreTryWaitForFunctor.hpp | 62 + .../SemaphoreTryWaitFunctor.hpp | 43 + .../SemaphoreTryWaitUntilFunctor.hpp | 63 + .../synchronization/SemaphoreWaitFunctor.hpp | 43 + .../SignalInformationQueue.hpp | 123 ++ .../SignalsCatcherControlBlock.hpp | 213 +++ .../SignalsReceiverControlBlock.hpp | 243 ++++ .../synchronization/SwapPopQueueFunctor.hpp | 74 + include/distortos/statistics.hpp | 38 + include/estd/ContiguousRange.hpp | 164 +++ include/estd/IntegerSequence.hpp | 229 ++++ include/estd/IntrusiveForwardList.hpp | 1120 +++++++++++++++ include/estd/IntrusiveList.hpp | 1208 +++++++++++++++++ include/estd/ReferenceHolder.hpp | 61 + include/estd/ReverseAdaptor.hpp | 83 ++ include/estd/SortedIntrusiveForwardList.hpp | 355 +++++ include/estd/SortedIntrusiveList.hpp | 353 +++++ include/estd/TypeErasedFunctor.hpp | 99 ++ include/estd/apply.hpp | 73 + include/estd/invoke.hpp | 147 ++ include/motor.h | 33 - include/sys/signal.h | 41 + libdistortos.a | Bin 0 -> 239010 bytes main.cpp | 126 -- mcuconf.h | 204 --- setup.mk | 15 - src/motor.cpp | 3 - vexuser.cpp | 207 --- 125 files changed, 16100 insertions(+), 1774 deletions(-) delete mode 100644 Makefile create mode 100644 STM32F103VD.ld delete mode 100644 chconf.h delete mode 100644 halconf.h delete mode 100644 include/control.h create mode 100644 include/distortos/ConditionVariable.hpp create mode 100644 include/distortos/DynamicFifoQueue.hpp create mode 100644 include/distortos/DynamicMessageQueue.hpp create mode 100644 include/distortos/DynamicRawFifoQueue.hpp create mode 100644 include/distortos/DynamicRawMessageQueue.hpp create mode 100644 include/distortos/DynamicSignalsReceiver.hpp create mode 100644 include/distortos/DynamicThread.hpp create mode 100644 include/distortos/DynamicThreadParameters.hpp create mode 100644 include/distortos/FifoQueue.hpp create mode 100644 include/distortos/MessageQueue.hpp create mode 100644 include/distortos/Mutex.hpp create mode 100644 include/distortos/OnceFlag.hpp create mode 100644 include/distortos/RawFifoQueue.hpp create mode 100644 include/distortos/RawMessageQueue.hpp create mode 100644 include/distortos/SchedulingPolicy.hpp create mode 100644 include/distortos/Semaphore.hpp create mode 100644 include/distortos/SignalAction.hpp create mode 100644 include/distortos/SignalInformation.hpp create mode 100644 include/distortos/SignalInformationQueueWrapper.hpp create mode 100644 include/distortos/SignalSet.hpp create mode 100644 include/distortos/SignalsCatcher.hpp create mode 100644 include/distortos/SignalsReceiver.hpp create mode 100644 include/distortos/SoftwareTimer.hpp create mode 100644 include/distortos/SoftwareTimerCommon.hpp create mode 100644 include/distortos/StaticFifoQueue.hpp create mode 100644 include/distortos/StaticMessageQueue.hpp create mode 100644 include/distortos/StaticRawFifoQueue.hpp create mode 100644 include/distortos/StaticRawMessageQueue.hpp create mode 100644 include/distortos/StaticSignalsReceiver.hpp create mode 100644 include/distortos/StaticSoftwareTimer.hpp create mode 100644 include/distortos/StaticThread.hpp create mode 100644 include/distortos/ThisThread-Signals.hpp create mode 100644 include/distortos/ThisThread.hpp create mode 100644 include/distortos/Thread.hpp create mode 100644 include/distortos/ThreadCommon.hpp create mode 100644 include/distortos/ThreadState.hpp create mode 100644 include/distortos/TickClock.hpp create mode 100644 include/distortos/UndetachableThread.hpp create mode 100644 include/distortos/architecture/InterruptMaskingLock.hpp create mode 100644 include/distortos/architecture/InterruptMaskingUnmaskingLock.hpp create mode 100644 include/distortos/architecture/InterruptUnmaskingLock.hpp create mode 100644 include/distortos/architecture/Stack.hpp create mode 100644 include/distortos/architecture/disableInterruptMasking.hpp create mode 100644 include/distortos/architecture/enableInterruptMasking.hpp create mode 100644 include/distortos/architecture/getMainStack.hpp create mode 100644 include/distortos/architecture/initializeStack.hpp create mode 100644 include/distortos/architecture/lowLevelInitialization.hpp create mode 100644 include/distortos/architecture/requestContextSwitch.hpp create mode 100644 include/distortos/architecture/requestFunctionExecution.hpp create mode 100644 include/distortos/architecture/restoreInterruptMasking.hpp create mode 100644 include/distortos/architecture/startScheduling.hpp create mode 100644 include/distortos/board/lowLevelInitialization.hpp create mode 100644 include/distortos/callOnce.hpp create mode 100644 include/distortos/chip/lowLevelInitialization.hpp create mode 100644 include/distortos/devices/io/InputPin.hpp create mode 100644 include/distortos/devices/io/OutputPin.hpp create mode 100644 include/distortos/distortosVersion.h create mode 100644 include/distortos/internal/memory/DeferredThreadDeleter.hpp create mode 100644 include/distortos/internal/memory/dummyDeleter.hpp create mode 100644 include/distortos/internal/memory/getDeferredThreadDeleter.hpp create mode 100644 include/distortos/internal/memory/getMallocMutex.hpp create mode 100644 include/distortos/internal/memory/storageDeleter.hpp create mode 100644 include/distortos/internal/scheduler/DynamicThreadBase.hpp create mode 100644 include/distortos/internal/scheduler/MainThread.hpp create mode 100644 include/distortos/internal/scheduler/RoundRobinQuantum.hpp create mode 100644 include/distortos/internal/scheduler/Scheduler.hpp create mode 100644 include/distortos/internal/scheduler/SoftwareTimerControlBlock.hpp create mode 100644 include/distortos/internal/scheduler/SoftwareTimerList.hpp create mode 100644 include/distortos/internal/scheduler/SoftwareTimerListNode.hpp create mode 100644 include/distortos/internal/scheduler/SoftwareTimerSupervisor.hpp create mode 100644 include/distortos/internal/scheduler/ThreadControlBlock.hpp create mode 100644 include/distortos/internal/scheduler/ThreadGroupControlBlock.hpp create mode 100644 include/distortos/internal/scheduler/ThreadList.hpp create mode 100644 include/distortos/internal/scheduler/ThreadListNode.hpp create mode 100644 include/distortos/internal/scheduler/forceContextSwitch.hpp create mode 100644 include/distortos/internal/scheduler/getScheduler.hpp create mode 100644 include/distortos/internal/scheduler/idleThreadFunction.hpp create mode 100644 include/distortos/internal/scheduler/lowLevelInitialization.hpp create mode 100644 include/distortos/internal/scheduler/threadRunner.hpp create mode 100644 include/distortos/internal/synchronization/BoundQueueFunctor.hpp create mode 100644 include/distortos/internal/synchronization/CallOnceControlBlock.hpp create mode 100644 include/distortos/internal/synchronization/CopyConstructQueueFunctor.hpp create mode 100644 include/distortos/internal/synchronization/FifoQueueBase.hpp create mode 100644 include/distortos/internal/synchronization/MemcpyPopQueueFunctor.hpp create mode 100644 include/distortos/internal/synchronization/MemcpyPushQueueFunctor.hpp create mode 100644 include/distortos/internal/synchronization/MessageQueueBase.hpp create mode 100644 include/distortos/internal/synchronization/MoveConstructQueueFunctor.hpp create mode 100644 include/distortos/internal/synchronization/MutexControlBlock.hpp create mode 100644 include/distortos/internal/synchronization/MutexList.hpp create mode 100644 include/distortos/internal/synchronization/MutexListNode.hpp create mode 100644 include/distortos/internal/synchronization/QueueFunctor.hpp create mode 100644 include/distortos/internal/synchronization/SemaphoreFunctor.hpp create mode 100644 include/distortos/internal/synchronization/SemaphoreTryWaitForFunctor.hpp create mode 100644 include/distortos/internal/synchronization/SemaphoreTryWaitFunctor.hpp create mode 100644 include/distortos/internal/synchronization/SemaphoreTryWaitUntilFunctor.hpp create mode 100644 include/distortos/internal/synchronization/SemaphoreWaitFunctor.hpp create mode 100644 include/distortos/internal/synchronization/SignalInformationQueue.hpp create mode 100644 include/distortos/internal/synchronization/SignalsCatcherControlBlock.hpp create mode 100644 include/distortos/internal/synchronization/SignalsReceiverControlBlock.hpp create mode 100644 include/distortos/internal/synchronization/SwapPopQueueFunctor.hpp create mode 100644 include/distortos/statistics.hpp create mode 100644 include/estd/ContiguousRange.hpp create mode 100644 include/estd/IntegerSequence.hpp create mode 100644 include/estd/IntrusiveForwardList.hpp create mode 100644 include/estd/IntrusiveList.hpp create mode 100644 include/estd/ReferenceHolder.hpp create mode 100644 include/estd/ReverseAdaptor.hpp create mode 100644 include/estd/SortedIntrusiveForwardList.hpp create mode 100644 include/estd/SortedIntrusiveList.hpp create mode 100644 include/estd/TypeErasedFunctor.hpp create mode 100644 include/estd/apply.hpp create mode 100644 include/estd/invoke.hpp delete mode 100644 include/motor.h create mode 100644 include/sys/signal.h create mode 100644 libdistortos.a delete mode 100644 main.cpp delete mode 100644 mcuconf.h delete mode 100644 setup.mk delete mode 100644 src/motor.cpp delete mode 100644 vexuser.cpp 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 - -#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(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 + 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 + int waitFor(Mutex& mutex, const std::chrono::duration duration) + { + return waitFor(mutex, std::chrono::duration_cast(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 + int waitFor(Mutex& mutex, const std::chrono::duration 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 + int waitUntil(Mutex& mutex, const std::chrono::time_point timePoint) + { + return waitUntil(mutex, std::chrono::time_point_cast(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 + int waitUntil(Mutex& mutex, std::chrono::time_point timePoint, Predicate predicate); + +private: + + /// ThreadControlBlock objects blocked on this condition variable + internal::ThreadList blockedList_; +}; + +template +int ConditionVariable::wait(Mutex& mutex, Predicate predicate) +{ + while (predicate() == false) + { + const auto ret = wait(mutex); + if (ret != 0) + return ret; + } + + return 0; +} + +template +int ConditionVariable::waitUntil(Mutex& mutex, const std::chrono::time_point 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 +class DynamicFifoQueue : public FifoQueue +{ +public: + + /// import Storage type from base class + using typename FifoQueue::Storage; + + /** + * \brief DynamicFifoQueue's constructor + * + * \param [in] queueSize is the maximum number of elements in queue + */ + + explicit DynamicFifoQueue(size_t queueSize); +}; + +template +DynamicFifoQueue::DynamicFifoQueue(const size_t queueSize) : + FifoQueue{{new Storage[queueSize], internal::storageDeleter}, 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 +class DynamicMessageQueue : public MessageQueue +{ +public: + + /// import EntryStorage type from base class + using typename MessageQueue::EntryStorage; + + /// import ValueStorage type from base class + using typename MessageQueue::ValueStorage; + + /** + * \brief DynamicMessageQueue's constructor + * + * \param [in] queueSize is the maximum number of elements in queue + */ + + explicit DynamicMessageQueue(size_t queueSize); +}; + +template +DynamicMessageQueue::DynamicMessageQueue(const size_t queueSize) : + MessageQueue{{new EntryStorage[queueSize], internal::storageDeleter}, + {new ValueStorage[queueSize], internal::storageDeleter}, 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 + 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 + DynamicThread(const DynamicThreadParameters parameters, Function&& function, Args&&... args) : + DynamicThread{parameters.stackSize, parameters.canReceiveSignals, parameters.queuedSignals, + parameters.signalActions, parameters.priority, parameters.schedulingPolicy, + std::forward(function), std::forward(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 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 +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), std::forward(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 +DynamicThread makeDynamicThread(const DynamicThreadParameters parameters, Function&& function, Args&&... args) +{ + return {parameters, std::forward(function), std::forward(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 +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), std::forward(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 +DynamicThread makeAndStartDynamicThread(const DynamicThreadParameters parameters, Function&& function, Args&&... args) +{ + auto thread = makeDynamicThread(parameters, std::forward(function), std::forward(args)...); + thread.start(); /// \todo make sure this never fails + return thread; +} + +/// \} + +#ifdef CONFIG_THREAD_DETACH_ENABLE + +template +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), std::forward(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 + +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 +class FifoQueue +{ +public: + + /// type of uninitialized storage for data + using Storage = typename std::aligned_storage::type; + + /// unique_ptr (with deleter) to Storage[] + using StorageUniquePointer = + std::unique_ptr; + + /** + * \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 + int emplace(Args&&... args) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return emplaceInternal(semaphoreWaitFunctor, std::forward(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 + int tryEmplace(Args&&... args) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return emplaceInternal(semaphoreTryWaitFunctor, std::forward(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 + int tryEmplaceFor(const TickClock::duration duration, Args&&... args) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return emplaceInternal(semaphoreTryWaitForFunctor, std::forward(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 + int tryEmplaceFor(const std::chrono::duration duration, Args&&... args) + { + return tryEmplaceFor(std::chrono::duration_cast(duration), std::forward(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 + int tryEmplaceUntil(const TickClock::time_point timePoint, Args&&... args) + { + const internal::SemaphoreTryWaitUntilFunctor semaphoreTryWaitUntilFunctor {timePoint}; + return emplaceInternal(semaphoreTryWaitUntilFunctor, std::forward(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 + int tryEmplaceUntil(const std::chrono::time_point timePoint, Args&&... args) + { + return tryEmplaceUntil(std::chrono::time_point_cast(timePoint), + std::forward(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 + int tryPopFor(const std::chrono::duration duration, T& value) + { + return tryPopFor(std::chrono::duration_cast(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 + int tryPopUntil(const std::chrono::time_point timePoint, T& value) + { + return tryPopUntil(std::chrono::time_point_cast(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 + int tryPushFor(const std::chrono::duration duration, const T& value) + { + return tryPushFor(std::chrono::duration_cast(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 + int tryPushFor(const std::chrono::duration duration, T&& value) + { + return tryPushFor(std::chrono::duration_cast(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 + int tryPushUntil(const std::chrono::time_point timePoint, const T& value) + { + return tryPushUntil(std::chrono::time_point_cast(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 + int tryPushUntil(const std::chrono::time_point timePoint, T&& value) + { + return tryPushUntil(std::chrono::time_point_cast(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 + 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 +FifoQueue::~FifoQueue() +{ + T value; + while (tryPop(value) == 0); +} + +#if DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + +template +template +int FifoQueue::emplaceInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, Args&&... args) +{ + const auto emplaceFunctor = internal::makeBoundQueueFunctor( + [&args...](void* const storage) + { + new (storage) T{std::forward(args)...}; + }); + return fifoQueueBase_.push(waitSemaphoreFunctor, emplaceFunctor); +} + +#endif // DISTORTOS_FIFOQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + +template +int FifoQueue::popInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, T& value) +{ + const internal::SwapPopQueueFunctor swapPopQueueFunctor {value}; + return fifoQueueBase_.pop(waitSemaphoreFunctor, swapPopQueueFunctor); +} + +template +int FifoQueue::pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const T& value) +{ + const internal::CopyConstructQueueFunctor copyConstructQueueFunctor {value}; + return fifoQueueBase_.push(waitSemaphoreFunctor, copyConstructQueueFunctor); +} + +template +int FifoQueue::pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, T&& value) +{ + const internal::MoveConstructQueueFunctor 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 +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; + + /// import EntryStorageUniquePointer type from internal::MessageQueueBase class + using EntryStorageUniquePointer = internal::MessageQueueBase::EntryStorageUniquePointer; + + /// unique_ptr (with deleter) to ValueStorage[] + using ValueStorageUniquePointer = + std::unique_ptr; + + /** + * \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 + int emplace(const uint8_t priority, Args&&... args) + { + const internal::SemaphoreWaitFunctor semaphoreWaitFunctor; + return emplaceInternal(semaphoreWaitFunctor, priority, std::forward(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 + int tryEmplace(const uint8_t priority, Args&&... args) + { + const internal::SemaphoreTryWaitFunctor semaphoreTryWaitFunctor; + return emplaceInternal(semaphoreTryWaitFunctor, priority, std::forward(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 + int tryEmplaceFor(const TickClock::duration duration, const uint8_t priority, Args&&... args) + { + const internal::SemaphoreTryWaitForFunctor semaphoreTryWaitForFunctor {duration}; + return emplaceInternal(semaphoreTryWaitForFunctor, priority, std::forward(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 + int tryEmplaceFor(const std::chrono::duration duration, const uint8_t priority, Args&&... args) + { + return tryEmplaceFor(std::chrono::duration_cast(duration), priority, + std::forward(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 + 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)...); + } + + /** + * \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 + int tryEmplaceUntil(const std::chrono::time_point timePoint, const uint8_t priority, + Args&&... args) + { + return tryEmplaceUntil(std::chrono::time_point_cast(timePoint), priority, + std::forward(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 + int tryPopFor(const std::chrono::duration duration, uint8_t& priority, T& value) + { + return tryPopFor(std::chrono::duration_cast(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 + int tryPopUntil(const std::chrono::time_point timePoint, uint8_t& priority, T& value) + { + return tryPopUntil(std::chrono::time_point_cast(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 + int tryPushFor(const std::chrono::duration duration, const uint8_t priority, const T& value) + { + return tryPushFor(std::chrono::duration_cast(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 + int tryPushFor(const std::chrono::duration duration, const uint8_t priority, T&& value) + { + return tryPushFor(std::chrono::duration_cast(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 + int tryPushUntil(const std::chrono::time_point timePoint, const uint8_t priority, + const T& value) + { + return tryPushUntil(std::chrono::time_point_cast(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 + int tryPushUntil(const std::chrono::time_point timePoint, const uint8_t priority, T&& value) + { + return tryPushUntil(std::chrono::time_point_cast(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 + 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 +MessageQueue::~MessageQueue() +{ + uint8_t priority; + T value; + while (tryPop(priority, value) == 0); +} + +#if DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + +template +template +int MessageQueue::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)...}; + }); + return messageQueueBase_.push(waitSemaphoreFunctor, priority, emplaceFunctor); +} + +#endif // DISTORTOS_MESSAGEQUEUE_EMPLACE_SUPPORTED == 1 || DOXYGEN == 1 + +template +int MessageQueue::popInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, uint8_t& priority, T& value) +{ + const internal::SwapPopQueueFunctor swapPopQueueFunctor {value}; + return messageQueueBase_.pop(waitSemaphoreFunctor, priority, swapPopQueueFunctor); +} + +template +int MessageQueue::pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const uint8_t priority, + const T& value) +{ + const internal::CopyConstructQueueFunctor copyConstructQueueFunctor {value}; + return messageQueueBase_.push(waitSemaphoreFunctor, priority, copyConstructQueueFunctor); +} + +template +int MessageQueue::pushInternal(const internal::SemaphoreFunctor& waitSemaphoreFunctor, const uint8_t priority, + T&& value) +{ + const internal::MoveConstructQueueFunctor 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::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 + int tryLockFor(const std::chrono::duration duration) + { + return tryLockFor(std::chrono::duration_cast(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 + int tryLockUntil(const std::chrono::time_point timePoint) + { + return tryLockUntil(std::chrono::time_point_cast(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 + 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 + * std::is_trivially_copyable::value == true, 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 + 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 + 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 + 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 + int tryPopFor(const std::chrono::duration duration, void* const buffer, const size_t size) + { + return tryPopFor(std::chrono::duration_cast(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 + int tryPopFor(const std::chrono::duration duration, T& buffer) + { + return tryPopFor(std::chrono::duration_cast(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 + int tryPopUntil(const std::chrono::time_point timePoint, void* const buffer, const size_t size) + { + return tryPopUntil(std::chrono::time_point_cast(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 + int tryPopUntil(const std::chrono::time_point timePoint, T& buffer) + { + return tryPopUntil(std::chrono::time_point_cast(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 + 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 + int tryPushFor(const std::chrono::duration duration, const void* const data, const size_t size) + { + return tryPushFor(std::chrono::duration_cast(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 + int tryPushFor(const std::chrono::duration duration, const T& data) + { + return tryPushFor(std::chrono::duration_cast(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 + int tryPushUntil(const std::chrono::time_point timePoint, const void* const data, + const size_t size) + { + return tryPushUntil(std::chrono::time_point_cast(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 + int tryPushUntil(const std::chrono::time_point timePoint, const T& data) + { + return tryPushUntil(std::chrono::time_point_cast(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 + * std::is_trivially_copyable::value == true, 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 + using ValueStorage = internal::MessageQueueBase::ValueStorage; + + 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 + 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 + 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 + 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 + int tryPopFor(const std::chrono::duration duration, uint8_t& priority, void* const buffer, + const size_t size) + { + return tryPopFor(std::chrono::duration_cast(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 + int tryPopFor(const std::chrono::duration duration, uint8_t& priority, T& buffer) + { + return tryPopFor(std::chrono::duration_cast(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 + int tryPopUntil(const std::chrono::time_point timePoint, uint8_t& priority, void* const buffer, + const size_t size) + { + return tryPopUntil(std::chrono::time_point_cast(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 + int tryPopUntil(const std::chrono::time_point timePoint, uint8_t& priority, T& buffer) + { + return tryPopUntil(std::chrono::time_point_cast(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 + 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 + int tryPushFor(const std::chrono::duration duration, const uint8_t priority, const void* const data, + const size_t size) + { + return tryPushFor(std::chrono::duration_cast(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 + int tryPushFor(const std::chrono::duration duration, const uint8_t priority, const T& data) + { + return tryPushFor(std::chrono::duration_cast(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 + int tryPushUntil(const std::chrono::time_point timePoint, const uint8_t priority, + const void* const data, const size_t size) + { + return tryPushUntil(std::chrono::time_point_cast(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 + int tryPushUntil(const std::chrono::time_point timePoint, const uint8_t priority, + const T& data) + { + return tryPushUntil(std::chrono::time_point_cast(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 + +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::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 + int tryWaitFor(const std::chrono::duration duration) + { + return tryWaitFor(std::chrono::duration_cast(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 + int tryWaitUntil(const std::chrono::time_point timePoint) + { + return tryWaitUntil(std::chrono::time_point_cast(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 +#include + +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 + +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 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 + int start(const std::chrono::duration duration) + { + return start(std::chrono::duration_cast(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 + int start(const std::chrono::time_point timePoint) + { + return start(std::chrono::time_point_cast(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 +class StaticFifoQueue : public FifoQueue +{ +public: + + /// import Storage type from base class + using typename FifoQueue::Storage; + + /** + * \brief StaticFifoQueue's constructor + */ + + explicit StaticFifoQueue() : + FifoQueue{{storage_.data(), internal::dummyDeleter}, storage_.size()} + { + + } + +private: + + /// storage for queue's contents + std::array 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 +class StaticMessageQueue : public MessageQueue +{ +public: + + /// import EntryStorage type from base class + using typename MessageQueue::EntryStorage; + + /// import ValueStorage type from base class + using typename MessageQueue::ValueStorage; + + /** + * \brief StaticMessageQueue's constructor + */ + + explicit StaticMessageQueue() : + MessageQueue{{entryStorage_.data(), internal::dummyDeleter}, + {valueStorage_.data(), internal::dummyDeleter}, valueStorage_.size()} + { + + } + +private: + + /// storage for queue's entries + std::array entryStorage_; + + /// storage for queue's contents + std::array 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 +class StaticRawFifoQueue : public RawFifoQueue +{ +public: + + /// type of uninitialized storage for data + using Storage = typename std::aligned_storage::type; + + /** + * \brief StaticRawFifoQueue's constructor + */ + + explicit StaticRawFifoQueue() : + RawFifoQueue{{storage_.data(), internal::dummyDeleter}, sizeof(*storage_.data()), storage_.size()} + { + + } + +private: + + /// storage for queue's contents + std::array 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 +using StaticRawFifoQueueFromSize = + StaticRawFifoQueue::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 +class StaticRawMessageQueue : public RawMessageQueue +{ +public: + + /** + * \brief StaticRawMessageQueue's constructor + */ + + explicit StaticRawMessageQueue() : + RawMessageQueue{{entryStorage_.data(), internal::dummyDeleter}, + {valueStorage_.data(), internal::dummyDeleter>}, sizeof(*valueStorage_.data()), + valueStorage_.size()} + { + + } + +private: + + /// storage for queue's entries + std::array entryStorage_; + + /// storage for queue's contents + std::array, 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 +using StaticRawMessageQueueFromSize = + StaticRawMessageQueue::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 +class StaticSignalsReceiver : public SignalsReceiver +{ +public: + + /** + * \brief StaticSignalsReceiver's constructor + */ + + StaticSignalsReceiver() : + SignalsReceiver{&signalInformationQueueWrapper_, &signalsCatcher_}, + signalInformationQueueWrapper_{{signalInformationQueueWrapperStorage_.data(), + internal::dummyDeleter}, + signalInformationQueueWrapperStorage_.size()}, + signalsCatcher_{{signalsCatcherStorage_.data(), internal::dummyDeleter}, + signalsCatcherStorage_.size()} + { + + } + +private: + + /// storage for \a signalInformationQueueWrapper_ + std::array signalInformationQueueWrapperStorage_; + + /// internal SignalInformationQueueWrapper object + SignalInformationQueueWrapper signalInformationQueueWrapper_; + + /// storage for \a signalsCatcher_ + std::array 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 +class StaticSignalsReceiver : public SignalsReceiver +{ +public: + + /** + * \brief StaticSignalsReceiver's constructor + */ + + StaticSignalsReceiver() : + SignalsReceiver{&signalInformationQueueWrapper_, nullptr}, + signalInformationQueueWrapper_{{signalInformationQueueWrapperStorage_.data(), + internal::dummyDeleter}, + signalInformationQueueWrapperStorage_.size()} + { + + } + +private: + + /// storage for \a signalInformationQueueWrapper_ + std::array 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 +class StaticSignalsReceiver<0, SignalActions> : public SignalsReceiver +{ +public: + + /** + * \brief StaticSignalsReceiver's constructor + */ + + StaticSignalsReceiver() : + SignalsReceiver{nullptr, &signalsCatcher_}, + signalsCatcher_{{signalsCatcherStorage_.data(), internal::dummyDeleter}, + signalsCatcherStorage_.size()} + { + + } + +private: + + /// storage for \a signalsCatcher_ + std::array 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 + +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 +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), std::forward(args)...)} + { + + } + +private: + + /** + * \brief "Run" function of software timer + * + * Executes bound function object. + */ + + void run() override + { + boundFunction_(); + } + + /// bound function object + decltype(std::bind(std::declval(), std::declval()...)) 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 +StaticSoftwareTimer makeStaticSoftwareTimer(Function&& function, Args&&... args) +{ + return {std::forward(function), std::forward(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 +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), std::forward(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(thread).boundFunction_(); + } + + /// bound function object + decltype(std::bind(std::declval(), std::declval()...)) 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 +class StaticThread : public internal::StaticThreadBase +{ +public: + + /// base of StaticThread + using Base = internal::StaticThreadBase; + + /** + * \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), + std::forward(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::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 +class StaticThread : + public internal::StaticThreadBase +{ +public: + + /// base of StaticThread + using Base = internal::StaticThreadBase; + + /** + * \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), + std::forward(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::type stack_; + + /// internal StaticSignalsReceiver object + StaticSignalsReceiver 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 +StaticThread +makeStaticThread(const uint8_t priority, const SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args) +{ + return {priority, schedulingPolicy, std::forward(function), std::forward(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 +StaticThread +makeStaticThread(const uint8_t priority, Function&& function, Args&&... args) +{ + return {priority, std::forward(function), std::forward(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 +StaticThread +makeAndStartStaticThread(const uint8_t priority, const SchedulingPolicy schedulingPolicy, Function&& function, + Args&&... args) +{ + auto thread = makeStaticThread(priority, + schedulingPolicy, std::forward(function), std::forward(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 +StaticThread +makeAndStartStaticThread(const uint8_t priority, Function&& function, Args&&... args) +{ + auto thread = makeStaticThread(priority, + std::forward(function), std::forward(args)...); + thread.start(); /// \todo make sure this never fails + return thread; +} + +/// \} + +template +StaticThread:: +StaticThread(const uint8_t priority, const SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args) : + Base{{&stack_, internal::dummyDeleter}, sizeof(stack_), priority, schedulingPolicy, nullptr, + std::forward(function), std::forward(args)...} +{ + +} + +template +StaticThread::StaticThread(const uint8_t priority, + const SchedulingPolicy schedulingPolicy, Function&& function, Args&&... args) : + Base{{&stack_, internal::dummyDeleter}, sizeof(stack_), priority, schedulingPolicy, + &static_cast(staticSignalsReceiver_), std::forward(function), + std::forward(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 + +#include + +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 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 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 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 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 +std::pair tryWaitFor(const SignalSet& signalSet, + const std::chrono::duration duration) +{ + return tryWaitFor(signalSet, std::chrono::duration_cast(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 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 +std::pair tryWaitUntil(const SignalSet& signalSet, + const std::chrono::time_point timePoint) +{ + return tryWaitUntil(signalSet, std::chrono::time_point_cast(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 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 +int sleepFor(const std::chrono::duration duration) +{ + return sleepFor(std::chrono::duration_cast(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 +int sleepUntil(const std::chrono::time_point timePoint) +{ + return sleepUntil(std::chrono::time_point_cast(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 + +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 + +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 + +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; + + /// basic time_point type of clock + using time_point = std::chrono::time_point; + + /** + * \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 +{ + +}; + +} // 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 +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 +{ + +}; + +} // 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 + +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; + + /** + * \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 + +#include + +namespace distortos +{ + +namespace architecture +{ + +/** + * \brief Gets the stack used to run main(). + * + * \return beginning of stack and its size in bytes + */ + +std::pair 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 + +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 +void callOnce(OnceFlag& onceFlag, Function&& function, Args&&... args) +{ + onceFlag.callOnceControlBlock_(std::forward(function), std::forward(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 + +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 +void dummyDeleter(U*) +{ + static_assert(std::is_trivially_destructible::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 +void storageDeleter(U* const storage) +{ + delete[] reinterpret_cast(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 + 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 + 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 + DynamicThreadBase(const DynamicThreadParameters parameters, Function&& function, Args&&... args) : + DynamicThreadBase{parameters.stackSize, parameters.canReceiveSignals, parameters.queuedSignals, + parameters.signalActions, parameters.priority, parameters.schedulingPolicy, + std::forward(function), std::forward(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 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 +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}, 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), std::forward(args)...)}, + owner_{&owner} +{ + +} + +#else // !def CONFIG_THREAD_DETACH_ENABLE + +template +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}, 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), std::forward(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; + + /** + * \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; + +} // 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 + { + + }; + + /** + * \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; + + /// 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 +{ + +}; + +} // 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 + +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 void* as only argument + */ + +template +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 void* 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 +constexpr BoundQueueFunctor makeBoundQueueFunctor(F&& boundFunctor) +{ + return BoundQueueFunctor{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 + +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 + 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 + { + + }; + + /** + * \brief BoundedFunctor is a type-erased Functor which calls its bounded functor + * + * \tparam F is the type of bounded functor + */ + + template + 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 + constexpr static BoundedFunctor makeBoundedFunctor(F&& boundedFunctor) + { + return BoundedFunctor{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 +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), std::forward(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 +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 + +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; + + /** + * \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 + +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 + +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 + +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::type; + + /// unique_ptr (with deleter) to EntryStorage[] + using EntryStorageUniquePointer = std::unique_ptr; + + /** + * type of uninitialized storage for value + * + * \tparam T is the type of data in queue + */ + + template + using ValueStorage = typename std::aligned_storage::type; + + /// unique_ptr (with deleter) to storage + using ValueStorageUniquePointer = std::unique_ptr; + + /// 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; + + /// 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 + { + + }; + + /** + * \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 + +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 +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; + +} // 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 +{ + +}; + +} // 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 +{ + +}; + +} // 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 + +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::type; + + /// unique_ptr (with deleter) to Storage[] + using StorageUniquePointer = std::unique_ptr; + + /** + * \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 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; + + /// 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 + +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; + + /// type of uninitialized storage for Association objects + using Storage = std::aligned_storage::type; + + /// unique_ptr (with deleter) to Storage[] + using StorageUniquePointer = std::unique_ptr; + + /** + * \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 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 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 [associationsBegin_; associationsEnd_) + * 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(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 + +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 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 (deliverSignals()). + * + * \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 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 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 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 + +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 +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(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 + +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 + +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 +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; + + /// const_reverse_iterator type + using const_reverse_iterator = std::reverse_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 + 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 + +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 +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 +using IndexSequence = IntegerSequence; + +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 +struct TypedSequence : IntegerSequence +{ + /// type of base class + using base = IntegerSequence; + + /// 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 +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 +struct DoubledIntegerSequence> +{ + /// TypedSequence with doubled number of elements - TypedSequence is turned into + /// TypedSequence + using type = TypedSequence; +}; + +/** + * \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 +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 +struct ExtendedIntegerSequence> +{ + /// sequence extended by one element - TypedSequence is turned into + /// TypedSequence + using type = TypedSequence; +}; + +/** + * \brief Implementation of generator of IntegerSequence types + * + * Generates TypedSequence 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 +struct MakeIntegerSequenceImplementation : + ExtendedIntegerSequence::type>::type> +{ + +}; + +/** + * \brief Implementation of generator of IntegerSequence types + * + * Specialization for terminal case - 0 elements - generates TypedSequence type. + * + * \tparam T is an integer type to use for the elements of the sequence + */ + +template +struct MakeIntegerSequenceImplementation +{ + /// empty TypedSequence type + using type = TypedSequence; +}; + +/** + * \brief Wrapper for MakeIntegerSequenceImplementation that ensures \a N is non-negative + * + * Generates TypedSequence 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 +struct MakeIntegerSequenceImplementationWrapper : + std::enable_if= 0, MakeIntegerSequenceImplementation(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 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 +using MakeIntegerSequence = typename internal::MakeIntegerSequenceImplementationWrapper::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 +using MakeIndexSequence = MakeIntegerSequence; + +/** + * \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 +using IndexSequenceFor = MakeIndexSequence; + +} // 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 + +#include + +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 +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::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::value == true, "U must be implicitly convertible to T!"); + + const auto offset = reinterpret_cast(&(static_cast(nullptr)->*NodePointer)); + return reinterpret_cast(reinterpret_cast(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 +inline bool operator!=(const IntrusiveForwardListIterator& left, + const IntrusiveForwardListIterator& 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 +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::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 + constexpr + IntrusiveForwardListConstIterator(const IntrusiveForwardListIterator& 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::value == true, "U must be implicitly convertible to T!"); + + const auto offset = reinterpret_cast(&(static_cast(nullptr)->*NodePointer)); + return reinterpret_cast(reinterpret_cast(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 +inline bool operator!=(const IntrusiveForwardListConstIterator& left, + const IntrusiveForwardListConstIterator& 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 +inline bool operator==(const IntrusiveForwardListIterator& left, + const IntrusiveForwardListConstIterator& 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 +inline bool operator!=(const IntrusiveForwardListIterator& left, + const IntrusiveForwardListConstIterator& 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 +inline bool operator!=(const IntrusiveForwardListConstIterator& left, + const IntrusiveForwardListIterator& 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 +class IntrusiveForwardList +{ +public: + + /// const iterator of elements on the list + using const_iterator = IntrusiveForwardListConstIterator; + + /// 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; + + /// 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::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 +inline void swap(IntrusiveForwardList& left, IntrusiveForwardList& 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 + +#include + +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 +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::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::value == true, "U must be implicitly convertible to T!"); + + const auto offset = reinterpret_cast(&(static_cast(nullptr)->*NodePointer)); + return reinterpret_cast(reinterpret_cast(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 +inline bool operator!=(const IntrusiveListIterator& left, + const IntrusiveListIterator& 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 +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::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 + constexpr IntrusiveListConstIterator(const IntrusiveListIterator& 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::value == true, "U must be implicitly convertible to T!"); + + const auto offset = reinterpret_cast(&(static_cast(nullptr)->*NodePointer)); + return reinterpret_cast(reinterpret_cast(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 +inline bool operator!=(const IntrusiveListConstIterator& left, + const IntrusiveListConstIterator& 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 +inline bool operator==(const IntrusiveListIterator& left, + const IntrusiveListConstIterator& 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 +inline bool operator!=(const IntrusiveListIterator& left, + const IntrusiveListConstIterator& 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 +inline bool operator!=(const IntrusiveListConstIterator& left, + const IntrusiveListIterator& 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 +class IntrusiveList +{ +public: + + /// const iterator of elements on the list + using const_iterator = IntrusiveListConstIterator; + + /// const reverse iterator of elements on the list + using const_reverse_iterator = std::reverse_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; + + /// reverse iterator of elements on the list + using reverse_iterator = std::reverse_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::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 +inline void swap(IntrusiveList& left, IntrusiveList& 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 +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 +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 +ReverseAdaptor makeReverseAdaptor(T& container) +{ + return ReverseAdaptor{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 + +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 +class SortedIntrusiveForwardList +{ +public: + + /// unsorted intrusive forward list used internally + using UnsortedIntrusiveForwardList = IntrusiveForwardList; + + /// 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(*this), static_cast(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 +inline void swap(SortedIntrusiveForwardList& left, + SortedIntrusiveForwardList& 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 + +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 +class SortedIntrusiveList +{ +public: + + /// unsorted intrusive list used internally + using UnsortedIntrusiveList = IntrusiveList; + + /// 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(*this), static_cast(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 +inline void swap(SortedIntrusiveList& left, + SortedIntrusiveList& 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 +class TypeErasedFunctor; + +/** + * \brief TypeErasedFunctor class is an interface for type-erased functors. + * + * Overload with const operator()(). + * + * \tparam R is the type returned by TypeErasedFunctor::operator()() const + * \tparam Args are the types of arguments for TypeErasedFunctor::operator()() const + */ + +template +class TypeErasedFunctor +{ +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 TypeErasedFunctor::operator()() + * \tparam Args are the types of arguments for TypeErasedFunctor::operator()() + */ + +template +class TypeErasedFunctor +{ +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 + +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 +constexpr auto apply(Function&& function, Tuple&& tuple, estd::IndexSequence) -> + decltype(estd::invoke(std::forward(function), std::get(std::forward(tuple))...)) +{ + return estd::invoke(std::forward(function), std::get(std::forward(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 +constexpr auto apply(Function&& function, Tuple&& tuple) -> + decltype(internal::apply(std::forward(function), std::forward(tuple), + estd::MakeIndexSequence::type>{}>{})) +{ + return internal::apply(std::forward(function), std::forward(tuple), + estd::MakeIndexSequence::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 + +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 +inline auto invoke(Function&& function, Args&&... args) -> + decltype(std::forward(function)(std::forward(args)...)) +{ + return std::forward(function)(std::forward(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 +inline auto invoke(T Base::* memberFunction, Derived&& object, Args&&... args) -> + decltype((std::forward(object).*memberFunction)(std::forward(args)...)) +{ + return (std::forward(object).*memberFunction)(std::forward(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 +inline auto invoke(MemberFunction memberFunction, Pointer&& pointer, Args&&... args) -> + decltype(((*std::forward(pointer)).*memberFunction)(std::forward(args)...)) +{ + return ((*std::forward(pointer)).*memberFunction)(std::forward(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 +inline auto invoke(T Base::* dataMember, Derived&& object) -> decltype(std::forward(object).*dataMember) +{ + return std::forward(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 +inline auto invoke(DataMember dataMember, Pointer&& pointer) -> decltype((*std::forward(pointer)).*dataMember) +{ + return (*std::forward(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 +auto invoke(Function&& function, Args&&... args) -> + decltype(internal::invoke(std::forward(function), std::forward(args)...)) +{ + return internal::invoke(std::forward(function), std::forward(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 new file mode 100644 index 0000000000000000000000000000000000000000..54a0aa15bc291c1798ec78a3f5e14e25ba183642 GIT binary patch literal 239010 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 - -#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 - - 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 -#include - -#include "ch.h" // needs for all ChibiOS programs -#include "hal.h" // hardware abstraction layer header -#include "vex.h" // vex library header - -#include -#include - -//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; - -static std::array 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); - } -}*/ -- 2.39.5