Compare commits
58 Commits
access_log
...
mesosphere
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2efdee5cb8 | ||
|
|
240f455bc0 | ||
|
|
6642373795 | ||
|
|
1a924ad317 | ||
|
|
91662decf0 | ||
|
|
529024448e | ||
|
|
757aa30e74 | ||
|
|
fb4e0988b9 | ||
|
|
ad879ca327 | ||
|
|
efe7325af3 | ||
|
|
173bde2eca | ||
|
|
acf32f841c | ||
|
|
be3550d382 | ||
|
|
9c8f818c29 | ||
|
|
eb7e4153d1 | ||
|
|
86c43331eb | ||
|
|
baa34ddab5 | ||
|
|
d9c97983a3 | ||
|
|
b6bbc4f3e5 | ||
|
|
195da2e599 | ||
|
|
1b3b26c3af | ||
|
|
0fb40d1ef5 | ||
|
|
4238d2e97f | ||
|
|
7b726c3184 | ||
|
|
40b860c239 | ||
|
|
5222b429c5 | ||
|
|
2949d08eb2 | ||
|
|
6d898acc98 | ||
|
|
f72836d72c | ||
|
|
65b20d0685 | ||
|
|
d4241fd8ef | ||
|
|
504c74bc57 | ||
|
|
c47a9931d9 | ||
|
|
4a1021f220 | ||
|
|
ac6762bb6c | ||
|
|
e2d8316401 | ||
|
|
0a0c05481e | ||
|
|
cd1f74154d | ||
|
|
698fa9fcb0 | ||
|
|
a4419dfc41 | ||
|
|
600afa5f0f | ||
|
|
9b1fb0c6df | ||
|
|
08970a82ea | ||
|
|
a035be66bd | ||
|
|
9318ab10b2 | ||
|
|
dffb233423 | ||
|
|
e57203a7d4 | ||
|
|
7fde5fbe40 | ||
|
|
1684e1d35c | ||
|
|
edcd4cbc26 | ||
|
|
be17f1f494 | ||
|
|
672204c993 | ||
|
|
6166262b5c | ||
|
|
b492096aed | ||
|
|
cfeebbd1c9 | ||
|
|
4078c9a07d | ||
|
|
ed982877bd | ||
|
|
745fa84e5e |
152
mesosphere/Makefile
Normal file
152
mesosphere/Makefile
Normal file
@@ -0,0 +1,152 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(MESOSPHERE_BOARD),)
|
||||
export MESOSPHERE_BOARD := nintendo-switch
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
export CONFIG_DIR := $(CURDIR)/config
|
||||
ifeq ($(MESOSPHERE_BOARD),nintendo-switch)
|
||||
export BOARD_MAKE_DIR := $(CURDIR)/config/board/nintendo/switch
|
||||
export ARCH_MAKE_DIR := $(CURDIR)/config/arch/arm64
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(CONFIG_DIR)/rules.mk
|
||||
include $(CONFIG_DIR)/common.mk
|
||||
include $(ARCH_MAKE_DIR)/arch.mk
|
||||
include $(BOARD_MAKE_DIR)/board.mk
|
||||
|
||||
SOURCES := $(COMMON_SOURCES_DIRS) $(ARCH_SOURCE_DIRS) $(BOARD_SOURCE_DIRS)
|
||||
DATA := data
|
||||
INCLUDES := include ../common/include
|
||||
|
||||
DEFINES := $(COMMON_DEFINES) $(ARCH_DEFINES) $(BOARD_DEFINES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
SETTING := $(COMMON_SETTING) $(ARCH_SETTING) $(BOARD_SETTING)
|
||||
|
||||
CFLAGS := $(SETTING) $(DEFINES) $(COMMON_CFLAGS) $(ARCH_CFLAGS) $(BOARD_CFLAGS)
|
||||
CFLAGS += $(INCLUDE)
|
||||
|
||||
CXXFLAGS := $(CFLAGS) $(COMMON_CXXFLAGS) $(ARCH_CXXFLAGS) $(BOARD_CXXFLAGS)
|
||||
|
||||
ASFLAGS := -g $(SETTING)
|
||||
LDFLAGS = -specs=$(ARCH_MAKE_DIR)/linker.specs $(SETTING) $(COMMON_LDFLAGS)
|
||||
|
||||
LIBS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS :=
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).bin
|
||||
|
||||
$(OUTPUT).bin : $(OUTPUT).elf
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
%.elf:
|
||||
@echo linking $(notdir $@)
|
||||
$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
@$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
9
mesosphere/README.md
Normal file
9
mesosphere/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Mesosphère
|
||||
|
||||
**WORK IN PROGRESS**
|
||||
|
||||
Special thanks to:
|
||||
|
||||
* @gdkchan ([Ryujinx](https://github.com/Ryujinx/Ryujinx)'s author), without whom I would have been unable to understand many complex mechanisms of the Horizon kernel, such as the scheduler, etc. Ryujinx's kernel HLE is pretty accurate, and of course part of this work has strong similarites to Ryujinx's kernel code.
|
||||
* @fincs, who helped me in the kernel reverse-engineering process a lot as well, and with countless other things too.
|
||||
|
||||
25
mesosphere/config/arch/arm64/arch.mk
Normal file
25
mesosphere/config/arch/arm64/arch.mk
Normal file
@@ -0,0 +1,25 @@
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
|
||||
PREFIX := aarch64-none-elf-
|
||||
|
||||
export CC := $(PREFIX)gcc
|
||||
export CXX := $(PREFIX)g++
|
||||
export AS := $(PREFIX)as
|
||||
export AR := $(PREFIX)gcc-ar
|
||||
export OBJCOPY := $(PREFIX)objcopy
|
||||
|
||||
ISVC=$(or $(VCBUILDHELPER_COMMAND),$(MSBUILDEXTENSIONSPATH32),$(MSBUILDEXTENSIONSPATH))
|
||||
|
||||
ifneq (,$(ISVC))
|
||||
ERROR_FILTER := 2>&1 | sed -e 's/\(.[a-zA-Z]\+\):\([0-9]\+\):/\1(\2):/g'
|
||||
endif
|
||||
|
||||
else
|
||||
include $(DEVKITPRO)/devkitA64/base_tools
|
||||
endif
|
||||
|
||||
ARCH_SETTING := -march=armv8-a -mgeneral-regs-only
|
||||
ARCH_DEFINES := -DMESOSPHERE_ARCH_ARM64
|
||||
ARCH_CFLAGS :=
|
||||
ARCH_CXXFLAGS :=
|
||||
ARCH_SOURCE_DIRS := source/arch/arm64
|
||||
214
mesosphere/config/arch/arm64/linker.ld
Normal file
214
mesosphere/config/arch/arm64/linker.ld
Normal file
@@ -0,0 +1,214 @@
|
||||
OUTPUT_ARCH(aarch64)
|
||||
ENTRY(_start)
|
||||
|
||||
/* TODO overhaul */
|
||||
|
||||
PHDRS
|
||||
{
|
||||
code PT_LOAD FLAGS(5) /* Read | Execute */;
|
||||
rodata PT_LOAD FLAGS(4) /* Read */;
|
||||
data PT_LOAD FLAGS(6) /* Read | Write */;
|
||||
dyn PT_DYNAMIC;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* =========== CODE section =========== */
|
||||
PROVIDE(__start__ = 0x0);
|
||||
. = __start__;
|
||||
|
||||
.crt0 :
|
||||
{
|
||||
KEEP (*(.crt0))
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.init :
|
||||
{
|
||||
KEEP( *(.init) )
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.plt :
|
||||
{
|
||||
*(.plt)
|
||||
*(.iplt)
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
||||
*(.text.exit .text.exit.*)
|
||||
*(.text.startup .text.startup.*)
|
||||
*(.text.hot .text.hot.*)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP( *(.fini) )
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
/* =========== RODATA section =========== */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
. = ALIGN(8);
|
||||
} :rodata
|
||||
|
||||
.eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } :rodata
|
||||
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } :rodata
|
||||
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :rodata
|
||||
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata
|
||||
|
||||
.dynamic : { *(.dynamic) } :rodata :dyn
|
||||
.dynsym : { *(.dynsym) } :rodata
|
||||
.dynstr : { *(.dynstr) } :rodata
|
||||
.rela.dyn : { *(.rela.*) } :rodata
|
||||
.interp : { *(.interp) } :rodata
|
||||
.hash : { *(.hash) } :rodata
|
||||
.gnu.hash : { *(.gnu.hash) } :rodata
|
||||
.gnu.version : { *(.gnu.version) } :rodata
|
||||
.gnu.version_d : { *(.gnu.version_d) } :rodata
|
||||
.gnu.version_r : { *(.gnu.version_r) } :rodata
|
||||
.note.gnu.build-id : { *(.note.gnu.build-id) } :rodata
|
||||
|
||||
/* =========== DATA section =========== */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } :data
|
||||
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } :data
|
||||
.gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } : data
|
||||
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } :data
|
||||
|
||||
.tdata ALIGN(8) :
|
||||
{
|
||||
__tdata_lma = .;
|
||||
*(.tdata .tdata.* .gnu.linkonce.td.*)
|
||||
. = ALIGN(8);
|
||||
__tdata_lma_end = .;
|
||||
} :data
|
||||
|
||||
.tbss ALIGN(8) :
|
||||
{
|
||||
*(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon)
|
||||
. = ALIGN(8);
|
||||
} :data
|
||||
|
||||
.preinit_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
} :data
|
||||
|
||||
.init_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE (__init_array_end = .);
|
||||
} :data
|
||||
|
||||
.fini_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE (__fini_array_end = .);
|
||||
} :data
|
||||
|
||||
.ctors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} :data
|
||||
|
||||
.dtors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} :data
|
||||
|
||||
__got_start__ = .;
|
||||
|
||||
.got : { *(.got) *(.igot) } :data
|
||||
.got.plt : { *(.got.plt) *(.igot.plt) } :data
|
||||
|
||||
__got_end__ = .;
|
||||
|
||||
.data ALIGN(8) :
|
||||
{
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
} :data
|
||||
|
||||
__bss_start__ = .;
|
||||
.bss ALIGN(8) :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(8);
|
||||
|
||||
/* Reserve space for the TLS segment of the main thread */
|
||||
__tls_start = .;
|
||||
. += + SIZEOF(.tdata) + SIZEOF(.tbss);
|
||||
__tls_end = .;
|
||||
} : data
|
||||
__bss_end__ = .;
|
||||
|
||||
__end__ = ABSOLUTE(.) ;
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
__argdata__ = ABSOLUTE(.) ;
|
||||
|
||||
/* ==================
|
||||
==== Metadata ====
|
||||
================== */
|
||||
|
||||
/* Discard sections that difficult post-processing */
|
||||
/DISCARD/ : { *(.group .comment .note) }
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
|
||||
/* 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) }
|
||||
.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) }
|
||||
}
|
||||
4
mesosphere/config/arch/arm64/linker.specs
Normal file
4
mesosphere/config/arch/arm64/linker.specs
Normal file
@@ -0,0 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(ARCH_MAKE_DIR /linker.ld) -pie --gc-sections -z text -z nodynamic-undefined-weak --build-id=sha1
|
||||
5
mesosphere/config/board/nintendo/switch/board.mk
Normal file
5
mesosphere/config/board/nintendo/switch/board.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
BOARD_SETTING := -mtune=cortex-a57
|
||||
BOARD_DEFINES := -DMESOSPHERE_BOARD_NINTENDO_SWITCH -DMESOSPHERE_BOARD_COMMON_ARM_ARM64_CLOCK
|
||||
BOARD_CFLAGS :=
|
||||
BOARD_CXXFLAGS :=
|
||||
BOARD_SOURCE_DIRS :=
|
||||
9
mesosphere/config/common.mk
Normal file
9
mesosphere/config/common.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
COMMON_DEFINES := -DBOOST_DISABLE_ASSERTS
|
||||
COMMON_SOURCES_DIRS := source/core source/interfaces source/interrupts source/kresources\
|
||||
source/processes source/threading source
|
||||
COMMON_SETTING := -fPIE -g -nostdlib
|
||||
COMMON_CFLAGS := -Wall -Werror -Os -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv\
|
||||
-fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector
|
||||
COMMON_CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++17
|
||||
COMMON_ASFLAGS :=
|
||||
COMMON_LDFLAGS := -Wl,-Map,out.map
|
||||
37
mesosphere/config/rules.mk
Normal file
37
mesosphere/config/rules.mk
Normal file
@@ -0,0 +1,37 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
%.a:
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $@)
|
||||
@rm -f $@
|
||||
$(AR) -rc $@ $^
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.cpp
|
||||
@echo $(notdir $<)
|
||||
$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.c
|
||||
@echo $(notdir $<)
|
||||
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.s
|
||||
@echo $(notdir $<)
|
||||
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.S
|
||||
@echo $(notdir $<)
|
||||
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# canned command sequence for binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' -e 's/[^A-Za-z0-9_]/_/g')`"_end[];" > `(echo $(<F) | tr . _)`.h
|
||||
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' -e 's/[^A-Za-z0-9_]/_/g')`"[];" >> `(echo $(<F) | tr . _)`.h
|
||||
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' -e 's/[^A-Za-z0-9_]/_/g')`_size";" >> `(echo $(<F) | tr . _)`.h
|
||||
endef
|
||||
11
mesosphere/include/mesosphere/arch/KInterruptMaskGuard.hpp
Normal file
11
mesosphere/include/mesosphere/arch/KInterruptMaskGuard.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if 1 //defined MESOSPHERE_ARCH_ARM64
|
||||
|
||||
#include <mesosphere/arch/arm64/KInterruptMaskGuard.hpp>
|
||||
|
||||
#else
|
||||
|
||||
//#error "No arch defined"
|
||||
|
||||
#endif
|
||||
11
mesosphere/include/mesosphere/arch/KSpinLock.hpp
Normal file
11
mesosphere/include/mesosphere/arch/KSpinLock.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if 1 //defined MESOSPHERE_ARCH_ARM64
|
||||
|
||||
#include <mesosphere/arch/arm64/KSpinLock.hpp>
|
||||
|
||||
#else
|
||||
|
||||
//#error "No arch defined"
|
||||
|
||||
#endif
|
||||
11
mesosphere/include/mesosphere/arch/arch.hpp
Normal file
11
mesosphere/include/mesosphere/arch/arch.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if 1 //defined MESOSPHERE_ARCH_ARM64
|
||||
|
||||
#include <mesosphere/arch/arm64/arch.hpp>
|
||||
|
||||
#else
|
||||
|
||||
//#error "No arch defined"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/arch/arm64/arch.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
inline namespace arch
|
||||
{
|
||||
inline namespace arm64
|
||||
{
|
||||
|
||||
// Dummy. Should be platform-independent:
|
||||
|
||||
class KInterruptMaskGuard final {
|
||||
public:
|
||||
|
||||
using FlagsType = u64;
|
||||
|
||||
KInterruptMaskGuard()
|
||||
{
|
||||
flags = MaskInterrupts();
|
||||
}
|
||||
|
||||
~KInterruptMaskGuard()
|
||||
{
|
||||
RestoreInterrupts(flags);
|
||||
}
|
||||
|
||||
KInterruptMaskGuard(const KInterruptMaskGuard &) = delete;
|
||||
KInterruptMaskGuard(KInterruptMaskGuard &&) = delete;
|
||||
KInterruptMaskGuard &operator=(const KInterruptMaskGuard &) = delete;
|
||||
KInterruptMaskGuard &operator=(KInterruptMaskGuard &&) = delete;
|
||||
|
||||
private:
|
||||
u64 flags = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
103
mesosphere/include/mesosphere/arch/arm64/KSpinLock.hpp
Normal file
103
mesosphere/include/mesosphere/arch/arm64/KSpinLock.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
inline namespace arch
|
||||
{
|
||||
inline namespace arm64
|
||||
{
|
||||
|
||||
// This largely uses the Linux kernel spinlock code, which is more efficient than Nintendo's (serializing two u16s into an u32).
|
||||
class KSpinLock {
|
||||
|
||||
private:
|
||||
|
||||
struct alignas(4) Ticket {
|
||||
u16 owner, next;
|
||||
};
|
||||
|
||||
Ticket ticket;
|
||||
|
||||
public:
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
u32 tmp;
|
||||
Ticket lockval;
|
||||
|
||||
asm volatile(
|
||||
" prfm pstl1strm, %2\n"
|
||||
"1: ldaxr %w0, %2\n"
|
||||
" eor %w1, %w0, %w0, ror #16\n"
|
||||
" cbnz %w1, 2f\n"
|
||||
" add %w0, %w0, %3\n"
|
||||
" stxr %w1, %w0, %2\n"
|
||||
" cbnz %w1, 1b\n"
|
||||
"2:"
|
||||
: "=&r" (lockval), "=&r" (tmp), "+Q" (ticket)
|
||||
: "I" (1 << 16)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
return !tmp;
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
u32 tmp;
|
||||
Ticket lockval, newval;
|
||||
|
||||
asm volatile(
|
||||
// Atomically increment the next ticket.
|
||||
" prfm pstl1strm, %3\n"
|
||||
"1: ldaxr %w0, %3\n"
|
||||
" add %w1, %w0, %w5\n"
|
||||
" stxr %w2, %w1, %3\n"
|
||||
" cbnz %w2, 1b\n"
|
||||
|
||||
|
||||
// Did we get the lock?
|
||||
" eor %w1, %w0, %w0, ror #16\n"
|
||||
" cbz %w1, 3f\n"
|
||||
/*
|
||||
No: spin on the owner. Send a local event to avoid missing an
|
||||
unlock before the exclusive load.
|
||||
*/
|
||||
" sevl\n"
|
||||
"2: wfe\n"
|
||||
" ldaxrh %w2, %4\n"
|
||||
" eor %w1, %w2, %w0, lsr #16\n"
|
||||
" cbnz %w1, 2b\n"
|
||||
// We got the lock. Critical section starts here.
|
||||
"3:"
|
||||
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*&ticket)
|
||||
: "Q" (ticket.owner), "I" (1 << 16)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
u64 tmp;
|
||||
asm volatile(
|
||||
" ldrh %w1, %0\n"
|
||||
" add %w1, %w1, #1\n"
|
||||
" stlrh %w1, %0"
|
||||
: "=Q" (ticket.owner), "=&r" (tmp)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
|
||||
}
|
||||
KSpinLock() = default;
|
||||
KSpinLock(const KSpinLock &) = delete;
|
||||
KSpinLock(KSpinLock &&) = delete;
|
||||
KSpinLock &operator=(const KSpinLock &) = delete;
|
||||
KSpinLock &operator=(KSpinLock &&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
101
mesosphere/include/mesosphere/arch/arm64/arch.hpp
Normal file
101
mesosphere/include/mesosphere/arch/arm64/arch.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/preprocessor.hpp>
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
#define MESOSPHERE_READ_SYSREG(r) ({\
|
||||
u64 __val; \
|
||||
asm volatile("mrs %0, " BOOST_PP_STRINGIZE(r) : "=r" (__val)); \
|
||||
__val; \
|
||||
})
|
||||
|
||||
#define MESOSPHERE_WRITE_SYSREG(v, r) do { \
|
||||
u64 __val = (u64)v; \
|
||||
asm volatile("msr " BOOST_PP_STRINGIZE(r) ", %0" \
|
||||
:: "r" (__val) : "memory"); \
|
||||
} while (false)
|
||||
|
||||
#define MESOSPHERE_DAIF_BIT(v) (((u64)(v)) >> 6)
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KCoreContext;
|
||||
|
||||
inline namespace arch
|
||||
{
|
||||
inline namespace arm64
|
||||
{
|
||||
|
||||
enum PsrMode {
|
||||
PSR_MODE_EL0t = 0x0u,
|
||||
PSR_MODE_EL1t = 0x4u,
|
||||
PSR_MODE_EL1h = 0x5u,
|
||||
PSR_MODE_EL2t = 0x8u,
|
||||
PSR_MODE_EL2h = 0x9u,
|
||||
PSR_MODE_EL3t = 0xCu,
|
||||
PSR_MODE_EL3h = 0xDu,
|
||||
PSR_MODE_MASK = 0xFu,
|
||||
PSR_MODE32_BIT = 0x10u,
|
||||
};
|
||||
|
||||
enum PsrInterruptBit {
|
||||
PSR_F_BIT = 1u << 6,
|
||||
PSR_I_BIT = 1u << 7,
|
||||
PSR_A_BIT = 1u << 8,
|
||||
PSR_D_BIT = 1u << 9,
|
||||
};
|
||||
|
||||
enum PsrStatusBit {
|
||||
PSR_PAN_BIT = 1u << 22,
|
||||
PSR_UAO_BIT = 1u << 23,
|
||||
};
|
||||
|
||||
enum PsrFlagBit {
|
||||
PSR_V_BIT = 1u << 28,
|
||||
PSR_C_BIT = 1u << 29,
|
||||
PSR_Z_BIT = 1u << 30,
|
||||
PSR_N_BIT = 1u << 31,
|
||||
};
|
||||
|
||||
enum PsrBitGroup {
|
||||
PSR_c = 0x000000FFu,
|
||||
PSR_x = 0x0000FF00u,
|
||||
PSR_s = 0x00FF0000u,
|
||||
PSR_f = 0xFF000000u,
|
||||
};
|
||||
|
||||
using InterruptFlagsType = u64;
|
||||
|
||||
static inline InterruptFlagsType MaskInterrupts()
|
||||
{
|
||||
InterruptFlagsType flags = MESOSPHERE_READ_SYSREG(daif);
|
||||
MESOSPHERE_WRITE_SYSREG(flags | PSR_I_BIT, daif);
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline void RestoreInterrupts(InterruptFlagsType flags)
|
||||
{
|
||||
MESOSPHERE_WRITE_SYSREG(MESOSPHERE_READ_SYSREG(daif) | (flags & PSR_I_BIT), daif);
|
||||
}
|
||||
|
||||
static inline KCoreContext &GetCurrentCoreContextInstance()
|
||||
{
|
||||
register KCoreContext *x18 asm ("x18");
|
||||
return *x18;
|
||||
}
|
||||
|
||||
static inline void ReloadCurrentCoreContextInstance()
|
||||
{
|
||||
asm volatile("mrs x18, tpidr_el1" ::: "x18", "memory");
|
||||
}
|
||||
|
||||
static inline void SetCurrentCoreContextInstance(KCoreContext &cctx)
|
||||
{
|
||||
MESOSPHERE_WRITE_SYSREG((uiptr)&cctx, tpidr_el1);
|
||||
ReloadCurrentCoreContextInstance();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
11
mesosphere/include/mesosphere/board/KSystemClock.hpp
Normal file
11
mesosphere/include/mesosphere/board/KSystemClock.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if 1 //defined MESOSPHERE_ARCH_ARM64
|
||||
|
||||
#include <mesosphere/board/common/arm/arm64/timer/KSystemClock.hpp>
|
||||
|
||||
#else
|
||||
|
||||
//#error "No arch defined"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/arch/arm64/arch.hpp>
|
||||
|
||||
#ifndef MESOSPHERE_SYSTEM_CLOCK_RATE // NEEDS to be defined; depends on cntfreq
|
||||
#define MESOSPHERE_SYSTEM_CLOCK_RATE 192000000ull
|
||||
#endif
|
||||
|
||||
// Architectural aarch64 armv8 timer
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
inline namespace board
|
||||
{
|
||||
inline namespace common
|
||||
{
|
||||
inline namespace arm
|
||||
{
|
||||
inline namespace arm64
|
||||
{
|
||||
|
||||
// Dummy implementation
|
||||
// Needs to be changed for platform stuff
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/// Fulfills Clock named requirements
|
||||
class KSystemClock {
|
||||
public:
|
||||
|
||||
using rep = s64;
|
||||
using period = std::ratio<1, MESOSPHERE_SYSTEM_CLOCK_RATE>;
|
||||
using duration = std::chrono::duration<rep, period>;
|
||||
using time_point = std::chrono::time_point<KSystemClock>;
|
||||
|
||||
static constexpr bool is_steady = true;
|
||||
|
||||
static time_point now()
|
||||
{
|
||||
return time_point{duration::zero()};
|
||||
}
|
||||
|
||||
static constexpr bool isCorePrivate = true;
|
||||
static constexpr duration forever = duration{-1};
|
||||
static constexpr time_point never = time_point{forever};
|
||||
|
||||
static constexpr uint GetIrqId() { return 30; }
|
||||
|
||||
static void Disable()
|
||||
{
|
||||
// Note: still continues counting.
|
||||
MESOSPHERE_WRITE_SYSREG(0, cntp_ctl_el0);
|
||||
}
|
||||
|
||||
static void SetInterruptMasked(bool maskInterrupts)
|
||||
{
|
||||
u64 val = maskInterrupts ? 3 : 1; // Note: also enables the timer.
|
||||
MESOSPHERE_WRITE_SYSREG(val, cntp_ctl_el0);
|
||||
}
|
||||
|
||||
static void SetAlarm(const time_point &when)
|
||||
{
|
||||
u64 val = (u64)when.time_since_epoch().count();
|
||||
MESOSPHERE_WRITE_SYSREG(val, cntp_cval_el0);
|
||||
SetInterruptMasked(false);
|
||||
}
|
||||
|
||||
static void Initialize()
|
||||
{
|
||||
MESOSPHERE_WRITE_SYSREG(1, cntkctl_el1); // Trap register accesses from el0.
|
||||
Disable();
|
||||
MESOSPHERE_WRITE_SYSREG(UINT64_MAX, cntp_cval_el0);
|
||||
SetInterruptMasked(true);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
mesosphere/include/mesosphere/core/Handle.hpp
Normal file
29
mesosphere/include/mesosphere/core/Handle.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class Handle final {
|
||||
public:
|
||||
constexpr bool IsAliasOrFree() const { return isAlias || id < 0; }
|
||||
|
||||
constexpr bool operator==(const Handle &other) const
|
||||
{
|
||||
return index == other.index && id == other.id && isAlias == other.isAlias;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Handle &other) const { return !(*this == other); }
|
||||
|
||||
constexpr Handle() : index{0}, id{0}, isAlias{false} {}
|
||||
|
||||
private:
|
||||
friend class KHandleTable;
|
||||
constexpr Handle(u16 index, s16 id, bool isAlias = false) : index{index}, id{id}, isAlias{isAlias} {}
|
||||
u32 index : 15;
|
||||
s32 id : 16;
|
||||
u32 isAlias : 1;
|
||||
};
|
||||
|
||||
}
|
||||
165
mesosphere/include/mesosphere/core/KAutoObject.hpp
Normal file
165
mesosphere/include/mesosphere/core/KAutoObject.hpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
|
||||
#define MESOSPHERE_AUTO_OBJECT_TRAITS(BaseId, DerivedId)\
|
||||
using BaseClass = K##BaseId ;\
|
||||
static constexpr KAutoObject::TypeId typeId = KAutoObject::TypeId::DerivedId;\
|
||||
virtual ushort GetClassToken() const\
|
||||
{\
|
||||
return KAutoObject::GenerateClassToken<K##DerivedId >();\
|
||||
}\
|
||||
|
||||
#define MESOSPHERE_AUTO_OBJECT_FW_DECL(BaseId)\
|
||||
class K##BaseId;\
|
||||
void intrusive_ptr_add_ref(K##BaseId *obj);\
|
||||
void intrusive_ptr_release(K##BaseId *obj);
|
||||
|
||||
|
||||
#define MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(BaseId)\
|
||||
inline void intrusive_ptr_add_ref(K##BaseId *obj) { intrusive_ptr_add_ref((KAutoObject *)obj); }\
|
||||
inline void intrusive_ptr_release(K##BaseId *obj) { intrusive_ptr_release((KAutoObject *)obj); }
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Process);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(ResourceLimit);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Thread);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Event);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(ReadableEvent);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(WritableEvent);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(InterruptEvent);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(LightSession);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(LightClientSession);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(LightServerSession);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Port);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(ClientPort);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(ServerPort);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Debug);
|
||||
|
||||
class KAutoObject {
|
||||
public:
|
||||
|
||||
/// Class token for polymorphic type checking
|
||||
virtual ushort GetClassToken() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Comparison key for KObjectAllocator
|
||||
virtual u64 GetComparisonKey() const
|
||||
{
|
||||
return (u64)(uiptr)this;
|
||||
}
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~KAutoObject();
|
||||
|
||||
|
||||
/// Check if the offset is base class of T or T itself
|
||||
template<typename T>
|
||||
bool IsInstanceOf() const
|
||||
{
|
||||
ushort btoken = GenerateClassToken<T>();
|
||||
ushort dtoken = GetClassToken();
|
||||
|
||||
return (dtoken & btoken) == btoken;
|
||||
}
|
||||
|
||||
// Explicitely disable copy and move, and add default ctor
|
||||
KAutoObject() = default;
|
||||
KAutoObject(const KAutoObject &) = delete;
|
||||
KAutoObject(KAutoObject &&) = delete;
|
||||
KAutoObject &operator=(const KAutoObject &) = delete;
|
||||
KAutoObject &operator=(KAutoObject &&) = delete;
|
||||
|
||||
/// Type order as found in official kernel
|
||||
enum class TypeId : ushort {
|
||||
AutoObject = 0,
|
||||
SynchronizationObject,
|
||||
ReadableEvent,
|
||||
|
||||
FinalClassesMin = 3,
|
||||
|
||||
InterruptEvent = 3,
|
||||
Debug,
|
||||
ClientSession,
|
||||
Thread,
|
||||
Process,
|
||||
Session,
|
||||
ServerPort,
|
||||
ResourceLimit,
|
||||
SharedMemory,
|
||||
LightClientSession,
|
||||
ServerSession,
|
||||
LightSession,
|
||||
Event,
|
||||
LightServerSession,
|
||||
DeviceAddressSpace,
|
||||
ClientPort,
|
||||
Port,
|
||||
WritableEvent,
|
||||
TransferMemory,
|
||||
SessionRequest,
|
||||
CodeMemory, // JIT
|
||||
|
||||
Max = CodeMemory + 1,
|
||||
};
|
||||
|
||||
private:
|
||||
std::atomic<ulong> referenceCount{0}; // official kernel has u32 for this
|
||||
friend void intrusive_ptr_add_ref(KAutoObject *obj);
|
||||
friend void intrusive_ptr_release(KAutoObject *obj);
|
||||
|
||||
protected:
|
||||
|
||||
template<typename T>
|
||||
static constexpr ushort GenerateClassToken()
|
||||
{
|
||||
/* The token follows these following properties:
|
||||
* Multiple inheritance is not supported
|
||||
* (BaseToken & DerivedToken) == BaseToken
|
||||
* The token for KAutoObject is 0
|
||||
* Not-final classes have a token of (1 << (typeid - 1))
|
||||
* Final derived classes have a unique token part of Seq[typeid - DerivedClassMin] | 0x100,
|
||||
where Seq is (in base 2) 11, 101, 110, 1001, 1010, and so on...
|
||||
*/
|
||||
if constexpr (std::is_same_v<T, KAutoObject>) {
|
||||
return 0;
|
||||
} else if constexpr (!std::is_final_v<T>) {
|
||||
static_assert(T::typeId >= TypeId::SynchronizationObject && T::typeId < TypeId::FinalClassesMin, "Invalid type ID!");
|
||||
return (1 << ((ushort)T::typeId - 1)) | GenerateClassToken<typename T::BaseClass>();
|
||||
} else {
|
||||
static_assert(T::typeId >= TypeId::FinalClassesMin && T::typeId < TypeId::Max, "Invalid type ID!");
|
||||
ushort off = (ushort)T::typeId - (ushort)TypeId::FinalClassesMin;
|
||||
return ((ushort)detail::A038444(off) << 9) | 0x100u | GenerateClassToken<typename T::BaseClass>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline void intrusive_ptr_add_ref(KAutoObject *obj)
|
||||
{
|
||||
ulong oldval = obj->referenceCount.fetch_add(1);
|
||||
kassert(oldval + 1 != 0);
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_release(KAutoObject *obj)
|
||||
{
|
||||
ulong oldval = obj->referenceCount.fetch_sub(1);
|
||||
if (oldval - 1 == 0) {
|
||||
delete obj;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline SharedPtr<T> DynamicObjectCast(SharedPtr<KAutoObject> object) {
|
||||
if (object != nullptr && object->IsInstanceOf<T>()) {
|
||||
return boost::static_pointer_cast<T>(object);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
37
mesosphere/include/mesosphere/core/KCoreContext.hpp
Normal file
37
mesosphere/include/mesosphere/core/KCoreContext.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/arch/arch.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KProcess;
|
||||
class KThread;
|
||||
class KScheduler;
|
||||
class KAlarm;
|
||||
|
||||
class KCoreContext {
|
||||
public:
|
||||
static KCoreContext &GetInstance(uint coreId) { return instances[coreId]; };
|
||||
static KCoreContext &GetCurrentInstance() { return GetCurrentCoreContextInstance(); };
|
||||
|
||||
KThread *GetCurrentThread() const { return currentThread; }
|
||||
KProcess *GetCurrentProcess() const { return currentProcess; }
|
||||
KScheduler *GetScheduler() const { return scheduler; }
|
||||
KAlarm *GetAlarm() const { return alarm; }
|
||||
|
||||
KCoreContext(KScheduler *scheduler) : scheduler(scheduler) {}
|
||||
private:
|
||||
KThread *volatile currentThread = nullptr;
|
||||
KProcess *volatile currentProcess = nullptr;
|
||||
KScheduler *volatile scheduler = nullptr;
|
||||
KAlarm *volatile alarm = nullptr;
|
||||
|
||||
// more stuff
|
||||
|
||||
static std::array<KCoreContext, MAX_CORES> instances;
|
||||
};
|
||||
|
||||
}
|
||||
708
mesosphere/include/mesosphere/core/KLinkedList.hpp
Normal file
708
mesosphere/include/mesosphere/core/KLinkedList.hpp
Normal file
@@ -0,0 +1,708 @@
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/interfaces/ISlabAllocated.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class KLinkedList final {
|
||||
private:
|
||||
|
||||
|
||||
struct List final {
|
||||
struct Node final : public ISlabAllocated<Node> {
|
||||
|
||||
struct Link final {
|
||||
Link *prev = nullptr;
|
||||
Link *next = nullptr;
|
||||
|
||||
Node &parent()
|
||||
{
|
||||
return *detail::GetParentFromMember(this, &Node::link);
|
||||
}
|
||||
|
||||
const Node &parent() const
|
||||
{
|
||||
return *detail::GetParentFromMember(this, &Node::link);
|
||||
}
|
||||
|
||||
T &data() { return parent().data; }
|
||||
const T &data() const { return parent().data; }
|
||||
};
|
||||
|
||||
Link link{};
|
||||
T data{};
|
||||
|
||||
Node() = default;
|
||||
Node(const Node &other) = default;
|
||||
Node(Node &&other) = default;
|
||||
explicit Node(const T &data) : ISlabAllocated<Node>(), data{data} {}
|
||||
explicit Node(const T &&data) : ISlabAllocated<Node>(), data{data} {}
|
||||
template<typename ...Args>
|
||||
explicit Node(Args&& ...args) : ISlabAllocated<Node>(), data{std::forward<Args>(args)...} {}
|
||||
};
|
||||
|
||||
mutable typename Node::Link link{};
|
||||
typename Node::Link *last() const { return link.prev; }
|
||||
typename Node::Link *first() const { return link.next; }
|
||||
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
List list;
|
||||
|
||||
void insert_node_after(typename List::Node::Link *pos, typename List::Node::Link *nd)
|
||||
{
|
||||
// if pos is last & list is empty, ->next writes to first, etc.
|
||||
pos->next->prev = nd;
|
||||
nd->prev = pos;
|
||||
nd->next = pos->next;
|
||||
pos->next = nd;
|
||||
++list.size;
|
||||
}
|
||||
|
||||
void insert_node_before(typename List::Node::Link *pos, typename List::Node::Link *nd)
|
||||
{
|
||||
pos->prev->next = nd;
|
||||
nd->prev = pos->prev;
|
||||
nd->next = pos;
|
||||
pos->prev = nd;
|
||||
++list.size;
|
||||
}
|
||||
|
||||
void remove_node(typename List::Node::Link *nd)
|
||||
{
|
||||
nd->prev->next = nd->next;
|
||||
nd->next->prev = nd->prev;
|
||||
--list.size;
|
||||
}
|
||||
public:
|
||||
|
||||
template<bool isConst>
|
||||
class Iterator {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename KLinkedList::value_type;
|
||||
using difference_type = typename KLinkedList::difference_type;
|
||||
using pointer = typename std::conditional<
|
||||
isConst,
|
||||
typename KLinkedList::const_pointer,
|
||||
typename KLinkedList::pointer
|
||||
>::type;
|
||||
using reference = typename std::conditional<
|
||||
isConst,
|
||||
typename KLinkedList::const_reference,
|
||||
typename KLinkedList::reference
|
||||
>::type;
|
||||
|
||||
bool operator==(const Iterator &other) const
|
||||
{
|
||||
return node == other.node;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
return node->data();
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
{
|
||||
return &node->data();
|
||||
}
|
||||
|
||||
Iterator &operator++()
|
||||
{
|
||||
node = node->next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator &operator--() {
|
||||
node = node->prev;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator &operator++(int) {
|
||||
const Iterator v{*this};
|
||||
++(*this);
|
||||
return v;
|
||||
}
|
||||
|
||||
Iterator &operator--(int) {
|
||||
const Iterator v{*this};
|
||||
--(*this);
|
||||
return v;
|
||||
}
|
||||
|
||||
// allow implicit const->non-const
|
||||
Iterator(const Iterator<false> &other) : node{other.node} {}
|
||||
|
||||
friend class Iterator<true>;
|
||||
Iterator() = default;
|
||||
|
||||
private:
|
||||
friend class KLinkedList;
|
||||
|
||||
typename KLinkedList::List::Node::Link *node;
|
||||
|
||||
explicit Iterator(typename KLinkedList::List::Node::Link *node) : node{node} {}
|
||||
};
|
||||
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using reference = T &;
|
||||
using const_reference = const T &;
|
||||
using void_pointer = void *;
|
||||
using const_void_ptr = const void *;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using iterator = Iterator<false>;
|
||||
using const_iterator = Iterator<true>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
KLinkedList() : list{{&list.link, &list.link}} {};
|
||||
explicit KLinkedList(size_t count, const T &value) : KLinkedList()
|
||||
{
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
|
||||
explicit KLinkedList(size_t count) : KLinkedList()
|
||||
{
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto *nd = new typename List::Node;
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>>
|
||||
KLinkedList(InputIt first, InputIt last) : KLinkedList()
|
||||
{
|
||||
for (InputIt it = first; it != last; ++it) {
|
||||
auto *nd = new typename List::Node{*it};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
|
||||
void swap(KLinkedList &other) noexcept
|
||||
{
|
||||
using std::swap; // Enable ADL
|
||||
swap(list.link, other.list.link);
|
||||
swap(list.size, other.list.size);
|
||||
list.first()->prev = list.last()->next = &list.link;
|
||||
other.list.first()->prev = other.list.last()->next = &other.list.link;
|
||||
}
|
||||
|
||||
KLinkedList(KLinkedList &&other) noexcept : KLinkedList()
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
KLinkedList(std::initializer_list<T> ilist) : KLinkedList(ilist.begin(), ilist.end()) {}
|
||||
|
||||
void clear() noexcept
|
||||
{
|
||||
typename List::Node::Link *nxt;
|
||||
for (typename List::Node::Link *nd = list.first(); nd != &list.link; nd = nxt) {
|
||||
nxt = nd->next;
|
||||
delete &nd->parent();
|
||||
}
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
~KLinkedList()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
KLinkedList &operator=(KLinkedList other)
|
||||
{
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
KLinkedList &operator=(std::initializer_list<T> ilist)
|
||||
{
|
||||
KLinkedList tmp{ilist};
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void assign(size_t count, const T &value)
|
||||
{
|
||||
KLinkedList tmp{count, value};
|
||||
swap(tmp);
|
||||
}
|
||||
|
||||
template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>>
|
||||
void assign(InputIt first, InputIt last)
|
||||
{
|
||||
KLinkedList tmp{first, last};
|
||||
swap(tmp);
|
||||
}
|
||||
|
||||
void assign(std::initializer_list<T> ilist)
|
||||
{
|
||||
KLinkedList tmp{ilist};
|
||||
swap(tmp);
|
||||
}
|
||||
|
||||
T &front() { return list.first()->data(); }
|
||||
const T &front() const { return list.first()->data(); }
|
||||
|
||||
T &back() { return list.last()->data(); }
|
||||
const T &back() const { return list.last()->data(); }
|
||||
|
||||
const_iterator cbegin() const noexcept { return const_iterator{list.first()}; }
|
||||
const_iterator cend() const noexcept { return const_iterator{&list.link}; }
|
||||
|
||||
const_iterator begin() const noexcept { return cbegin(); }
|
||||
const_iterator end() const noexcept { return cend(); }
|
||||
|
||||
iterator begin() noexcept { return iterator{list.first()}; }
|
||||
iterator end() noexcept { return iterator{&list.link}; }
|
||||
|
||||
const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{list.last()}; }
|
||||
const_reverse_iterator crend() const noexcept { return const_reverse_iterator{&list.link}; }
|
||||
|
||||
const_reverse_iterator rbegin() const noexcept { return crbegin(); }
|
||||
const_reverse_iterator rend() const noexcept { return crend(); }
|
||||
|
||||
reverse_iterator rbegin() noexcept { return reverse_iterator{list.last()}; }
|
||||
reverse_iterator rend() noexcept { return reverse_iterator{&list.link}; }
|
||||
|
||||
KLinkedList(const KLinkedList &other) : KLinkedList(other.cbegin(), other.cend()) {}
|
||||
|
||||
constexpr size_t size() const noexcept { return list.size; }
|
||||
constexpr bool empty() const noexcept { return list.size == 0; }
|
||||
|
||||
iterator insert(const_iterator pos, const T &value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(pos.node, &nd->link);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator pos, T &&value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(pos.node, &nd->link);
|
||||
return iterator{&nd->link};
|
||||
}
|
||||
|
||||
iterator insert(const_iterator pos, size_t count, const T &value)
|
||||
{
|
||||
iterator ret = pos;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
ret = insert(ret, value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>>
|
||||
iterator insert(const_iterator pos, InputIt first, InputIt last)
|
||||
{
|
||||
// Note: our list definition allows --begin() to be well defined
|
||||
typename List::Node::Link *f = nullptr;
|
||||
typename List::Node::Link *p = pos.node->prev;
|
||||
for(InputIt it = first; it != last; ++it) {
|
||||
auto *nd = new typename List::Node{*it};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(p, &nd->link);
|
||||
p = &nd->link;
|
||||
f = f == nullptr ? p : f;
|
||||
}
|
||||
|
||||
return iterator{f};
|
||||
}
|
||||
|
||||
iterator insert(const_iterator pos, std::initializer_list<T> ilist)
|
||||
{
|
||||
return insert(pos, ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
iterator emplace(const_iterator pos, Args &&...args)
|
||||
{
|
||||
auto *nd = new typename List::Node{std::forward<Args>(args)...};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(pos.node, &nd->link);
|
||||
return iterator{&nd->link};
|
||||
}
|
||||
|
||||
iterator erase(const_iterator pos)
|
||||
{
|
||||
iterator ret{pos.node->next};
|
||||
remove_node(pos.node);
|
||||
delete &pos.node->parent();
|
||||
return ret;
|
||||
}
|
||||
|
||||
iterator erase(const_iterator first, const_iterator last)
|
||||
{
|
||||
const_iterator next;
|
||||
for (const_iterator it = first; it != last; it = next) {
|
||||
next = erase(it);
|
||||
}
|
||||
return iterator{next.node};
|
||||
}
|
||||
|
||||
void push_back(const T &value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
|
||||
void push_back(T &&value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
|
||||
template< class... Args>
|
||||
T &emplace_back(Args&&... args)
|
||||
{
|
||||
auto *nd = new typename List::Node{std::forward<Args>(args)...};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
return nd->data;
|
||||
}
|
||||
|
||||
template< class... Args>
|
||||
T *emplace_back_or_fail(Args&&... args)
|
||||
{
|
||||
auto *nd = new typename List::Node{std::forward<Args>(args)...};
|
||||
if (nd != nullptr) {
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
return &nd->data;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
auto *nd = list.last();
|
||||
remove_node(nd);
|
||||
delete &nd->parent();
|
||||
}
|
||||
|
||||
void push_front(const T &value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(list.first(), &nd->link);
|
||||
}
|
||||
|
||||
void push_front(T &&value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(list.first(), &nd->link);
|
||||
}
|
||||
|
||||
template< class... Args>
|
||||
T &emplace_front(Args&&... args)
|
||||
{
|
||||
auto *nd = new typename List::Node{std::forward<Args>(args)...};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(list.first(), &nd->link);
|
||||
return nd->data;
|
||||
}
|
||||
|
||||
void pop_front()
|
||||
{
|
||||
auto *nd = list.first();
|
||||
remove_node(nd);
|
||||
delete &nd->parent();
|
||||
}
|
||||
|
||||
void resize(size_t count)
|
||||
{
|
||||
if (count < list.size) {
|
||||
while (count < list.size) {
|
||||
pop_back();
|
||||
}
|
||||
} else {
|
||||
size_t s = list.size;
|
||||
for (size_t i = 0; i < count - s; i++) {
|
||||
auto *nd = new typename List::Node;
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_t count, const T &value)
|
||||
{
|
||||
if (count < list.size) {
|
||||
while (count < list.size) {
|
||||
pop_back();
|
||||
}
|
||||
} else {
|
||||
size_t s = list.size;
|
||||
for (size_t i = 0; i < count - s; i++) {
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &&other)
|
||||
{
|
||||
//auto *current = pos.node;
|
||||
auto *before = pos.node->prev;
|
||||
auto *after = pos.node; //current == &list.link ? current : pos.node->next;
|
||||
before->next = other.list.first();
|
||||
before->next->prev = before;
|
||||
after->prev = other.list.last();
|
||||
after->prev->next = after;
|
||||
|
||||
list.size += other.list.size;
|
||||
other.list.size = 0;
|
||||
other.list.link.prev = other.list.link.next = &other.list.link;
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &other)
|
||||
{
|
||||
splice(pos, std::move(other));
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &&other, const_iterator it)
|
||||
{
|
||||
other.remove_node(it.node);
|
||||
insert_node_before(pos.node, it.node);
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &other, const_iterator it)
|
||||
{
|
||||
splice(pos, std::move(other), it);
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &&other, const_iterator first, const_iterator last)
|
||||
{
|
||||
if (*this == other) {
|
||||
auto *current = pos.node;
|
||||
auto *before = pos.node->prev;
|
||||
auto *after = current == &list.link ? current : pos.node->next;
|
||||
before->next = first.node;
|
||||
before->next->prev = before;
|
||||
after->prev = last.node;
|
||||
after->prev->next = after;
|
||||
} else {
|
||||
// Note: the first, last version can't be O(1) because std::distance is O(n)...
|
||||
const_iterator next;
|
||||
for (const_iterator it = first; it != last; it = next) {
|
||||
next = it;
|
||||
++next;
|
||||
splice(pos, other, it);
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &other, const_iterator first, const_iterator last)
|
||||
{
|
||||
splice(pos, std::move(other), first, last);
|
||||
}
|
||||
|
||||
size_t remove(const T &value)
|
||||
{
|
||||
size_t n = 0;
|
||||
const_iterator next;
|
||||
for (const_iterator it = cbegin(); it != cend(); ) {
|
||||
if (*it == value) {
|
||||
it = erase(it);
|
||||
++n;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template<typename UnaryPredicate>
|
||||
size_t remove_if(UnaryPredicate p)
|
||||
{
|
||||
const_iterator next;
|
||||
size_t n = 0;
|
||||
for (const_iterator it = cbegin(); it != cend(); ) {
|
||||
if (p(*it)) {
|
||||
it = erase(it);
|
||||
++n;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template<typename Compare>
|
||||
void merge(KLinkedList &&other, Compare p)
|
||||
{
|
||||
typename List::Node::Link hd{};
|
||||
auto *cur = &hd;
|
||||
size_t n = 0;
|
||||
while (list.size > 0 && other.list.size > 0) {
|
||||
if (p(list.first()->data(), other.list.first()->data())) {
|
||||
cur->next = list.first();
|
||||
remove_node(list.first());
|
||||
} else {
|
||||
cur->next = other.list.first();
|
||||
other.remove_node(other.list.first());
|
||||
}
|
||||
cur->next->prev = cur;
|
||||
cur = cur->next;
|
||||
n++;
|
||||
}
|
||||
|
||||
// Steal the remaining elements
|
||||
if (list.size > 0) {
|
||||
cur->next = list.first();
|
||||
list.first()->prev = cur;
|
||||
cur = list.last();
|
||||
n += list.size;
|
||||
} else if (other.list.size > 0) {
|
||||
cur->next = other.list.first();
|
||||
other.list.first()->prev = cur;
|
||||
cur = other.list.last();
|
||||
n += other.list.size;
|
||||
}
|
||||
|
||||
|
||||
// Reset the other list to put it in a valid state
|
||||
other.list.link.prev = other.list.link.next = &other.list.link;
|
||||
other.list.size = 0;
|
||||
|
||||
// Finally, normalize the result and assign it to this
|
||||
list.link.next = hd.next;
|
||||
list.link.prev = cur;
|
||||
list.size = n;
|
||||
list.first()->prev = list.last()->next = &list.link;
|
||||
}
|
||||
|
||||
void merge(KLinkedList &&other)
|
||||
{
|
||||
merge(other, std::less<T>{});
|
||||
}
|
||||
|
||||
template<typename Compare>
|
||||
void merge(KLinkedList &other, Compare p)
|
||||
{
|
||||
merge(std::move(other), p);
|
||||
}
|
||||
|
||||
void merge(KLinkedList &other)
|
||||
{
|
||||
merge(other, std::less<T>{});
|
||||
}
|
||||
|
||||
void reverse() noexcept
|
||||
{
|
||||
typename List::Node::Link hd{};
|
||||
auto *cur = &hd;
|
||||
size_t n = 0;
|
||||
|
||||
while (list.size > 0) {
|
||||
cur->next = list.last();
|
||||
remove_node(list.last());
|
||||
cur->next->prev = cur;
|
||||
n++;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
list.link.next = hd.next;
|
||||
list.link.prev = cur;
|
||||
list.size = n;
|
||||
list.first()->prev = list.last()->next = &list.link;
|
||||
}
|
||||
|
||||
|
||||
template<typename BinaryPredicate>
|
||||
size_t unique(BinaryPredicate p)
|
||||
{
|
||||
typename List::Node::Link *nxt;
|
||||
size_t n = 0;
|
||||
for (auto *nd = list.first(); nd != &list.link; nd = nxt) {
|
||||
nxt = nd->next;
|
||||
for (auto *nd2 = nxt; nd2 != &list.link && p(nd->data(), nd2->data()); nd2 = nxt) {
|
||||
nxt = nd2->next;
|
||||
remove_node(nd2);
|
||||
delete &nd2->parent();
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t unique()
|
||||
{
|
||||
return unique(std::equal_to<T>{});
|
||||
}
|
||||
|
||||
// sort: a PITA to implement and not needed anyway
|
||||
|
||||
friend bool operator==(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
|
||||
}
|
||||
|
||||
friend bool operator!=(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend bool operator<(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::less<T>{});
|
||||
}
|
||||
|
||||
friend bool operator>(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::greater<T>{});
|
||||
}
|
||||
|
||||
friend bool operator<=(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::less_equal<T>{});
|
||||
}
|
||||
|
||||
friend bool operator>=(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::greater_equal<T>{});
|
||||
}
|
||||
|
||||
friend void swap(KLinkedList &lhs, KLinkedList &rhs)
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename InputIt>
|
||||
KLinkedList(InputIt b, InputIt e) -> KLinkedList<typename std::iterator_traits<InputIt>::value_type>;
|
||||
|
||||
}
|
||||
43
mesosphere/include/mesosphere/core/KObjectRegistry.hpp
Normal file
43
mesosphere/include/mesosphere/core/KObjectRegistry.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/core/KLinkedList.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/threading/KMutex.hpp>
|
||||
#include <cstring>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KObjectRegistry {
|
||||
public:
|
||||
|
||||
static KObjectRegistry &GetInstance() { return instance; }
|
||||
|
||||
SharedPtr<KAutoObject> Find(const char *name) const;
|
||||
Result Register(SharedPtr<KAutoObject> obj, const char *name);
|
||||
Result Unregister(const char *name);
|
||||
|
||||
private:
|
||||
|
||||
struct Node {
|
||||
SharedPtr<KAutoObject> obj{};
|
||||
char name[12] = {0};
|
||||
|
||||
Node() = default;
|
||||
Node(SharedPtr<KAutoObject> &&obj, const char *name) : obj{obj}
|
||||
{
|
||||
std::strncpy(this->name, name, sizeof(this->name));
|
||||
}
|
||||
};
|
||||
|
||||
const Node *FindImpl(const char *name) const;
|
||||
Node *FindImpl(const char *name);
|
||||
|
||||
KLinkedList<Node> nameNodes{};
|
||||
mutable KMutex mutex{};
|
||||
|
||||
static KObjectRegistry instance;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/core/KLinkedList.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KSynchronizationObject : public KAutoObject {
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, SynchronizationObject);
|
||||
|
||||
virtual ~KSynchronizationObject();
|
||||
virtual bool IsSignaled() const = 0;
|
||||
|
||||
void NotifyWaiters(); // Note: NotifyWaiters() with !IsSignaled() is no-op
|
||||
|
||||
KLinkedList<KThread *>::const_iterator AddWaiter(KThread &thread);
|
||||
void RemoveWaiter(KLinkedList<KThread *>::const_iterator it);
|
||||
|
||||
private:
|
||||
KLinkedList<KThread *> waiters{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(SynchronizationObject);
|
||||
|
||||
}
|
||||
143
mesosphere/include/mesosphere/core/Result.hpp
Normal file
143
mesosphere/include/mesosphere/core/Result.hpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
enum class ResultModule : uint {
|
||||
None = 0,
|
||||
Kernel = 1,
|
||||
/* Other modules not included. */
|
||||
};
|
||||
|
||||
class ResultHelper {
|
||||
public:
|
||||
using BaseType = uint;
|
||||
static constexpr BaseType SuccessValue = BaseType();
|
||||
static constexpr BaseType ModuleBits = 9;
|
||||
static constexpr BaseType DescriptionBits = 13;
|
||||
|
||||
template<ResultModule module, BaseType description>
|
||||
struct MakeResult : public std::integral_constant<BaseType, ((static_cast<BaseType>(module)) | (description << ModuleBits))> {
|
||||
static_assert(static_cast<BaseType>(module) < 1 << (ModuleBits + 1), "Invalid Module");
|
||||
static_assert(description < 1 << (DescriptionBits + 1), "Invalid Description");
|
||||
};
|
||||
|
||||
static constexpr ResultModule GetModule(BaseType value) {
|
||||
return static_cast<ResultModule>(value & ~(~BaseType() << ModuleBits));
|
||||
}
|
||||
|
||||
static constexpr BaseType GetDescription(BaseType value) {
|
||||
return ((value >> ModuleBits) & ~(~BaseType() << DescriptionBits));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Use CRTP for Results. */
|
||||
template<typename Self>
|
||||
class ResultBase {
|
||||
public:
|
||||
using BaseType = typename ResultHelper::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultHelper::SuccessValue;
|
||||
|
||||
constexpr bool IsSuccess() const { return static_cast<const Self *>(this)->GetValue() == SuccessValue; }
|
||||
constexpr bool IsFailure() const { return !IsSuccess(); }
|
||||
constexpr operator bool() const { return IsSuccess(); }
|
||||
constexpr bool operator !() const { return IsFailure(); }
|
||||
|
||||
constexpr ResultModule GetModule() const { return static_cast<const Self *>(this)->GetValue(); }
|
||||
constexpr BaseType GetDescription() const { return static_cast<const Self *>(this)->GetValue(); }
|
||||
};
|
||||
|
||||
/* Actual result type. */
|
||||
class Result final : public ResultBase<Result> {
|
||||
public:
|
||||
using BaseType = typename ResultBase<Result>::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultBase<Result>::SuccessValue;
|
||||
|
||||
constexpr Result() : value(SuccessValue) {}
|
||||
|
||||
constexpr BaseType GetValue() const { return this->value; }
|
||||
constexpr bool operator==(const Result &other) const { return value == other.value; }
|
||||
constexpr bool operator!=(const Result &other) const { return value != other.value; }
|
||||
|
||||
static constexpr Result MakeResult(BaseType v) { return Result(v); }
|
||||
|
||||
private:
|
||||
BaseType value;
|
||||
constexpr explicit Result(BaseType v) : value(v) {}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Result) == sizeof(Result::BaseType), "Bad Result definition!");
|
||||
|
||||
/* Successful result class. */
|
||||
class ResultSuccess final : public ResultBase<ResultSuccess> {
|
||||
public:
|
||||
using BaseType = typename ResultBase<ResultSuccess>::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultBase<ResultSuccess>::SuccessValue;
|
||||
|
||||
constexpr operator Result() { return Result::MakeResult(SuccessValue); }
|
||||
constexpr BaseType GetValue() const { return SuccessValue; }
|
||||
};
|
||||
|
||||
/* Error result class. */
|
||||
template<ResultModule module, ResultHelper::BaseType description>
|
||||
class ResultError : public ResultBase<ResultError<module, description>> {
|
||||
public:
|
||||
using BaseType = typename ResultBase<ResultError<module, description>>::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultBase<ResultError<module, description>>::SuccessValue;
|
||||
|
||||
static constexpr BaseType Value = ResultHelper::MakeResult<module, description>::value;
|
||||
static_assert(Value != SuccessValue, "Invalid ResultError");
|
||||
|
||||
constexpr operator Result() { return Result::MakeResult(Value); }
|
||||
constexpr BaseType GetValue() const { return Value; }
|
||||
};
|
||||
|
||||
#define DEFINE_RESULT(module, name, description) class Result##module##name final : public ResultError<ResultModule::module, description> {}
|
||||
|
||||
DEFINE_RESULT(Kernel, OutOfSessions, 7);
|
||||
|
||||
DEFINE_RESULT(Kernel, InvalidCapabilityDescriptor, 14);
|
||||
|
||||
DEFINE_RESULT(Kernel, NotImplemented, 33);
|
||||
DEFINE_RESULT(Kernel, ThreadTerminating, 59);
|
||||
|
||||
DEFINE_RESULT(Kernel, OutOfDebugEvents, 70);
|
||||
|
||||
DEFINE_RESULT(Kernel, InvalidSize, 101);
|
||||
DEFINE_RESULT(Kernel, InvalidAddress, 102);
|
||||
DEFINE_RESULT(Kernel, ResourceExhausted, 103);
|
||||
DEFINE_RESULT(Kernel, OutOfMemory, 104);
|
||||
DEFINE_RESULT(Kernel, OutOfHandles, 105);
|
||||
DEFINE_RESULT(Kernel, InvalidMemoryState, 106);
|
||||
DEFINE_RESULT(Kernel, InvalidMemoryPermissions, 108);
|
||||
DEFINE_RESULT(Kernel, InvalidMemoryRange, 110);
|
||||
DEFINE_RESULT(Kernel, InvalidPriority, 112);
|
||||
DEFINE_RESULT(Kernel, InvalidCoreId, 113);
|
||||
DEFINE_RESULT(Kernel, InvalidHandle, 114);
|
||||
DEFINE_RESULT(Kernel, InvalidUserBuffer, 115);
|
||||
DEFINE_RESULT(Kernel, InvalidCombination, 116);
|
||||
DEFINE_RESULT(Kernel, TimedOut, 117);
|
||||
DEFINE_RESULT(Kernel, Cancelled, 118);
|
||||
DEFINE_RESULT(Kernel, OutOfRange, 119);
|
||||
DEFINE_RESULT(Kernel, InvalidEnumValue, 120);
|
||||
DEFINE_RESULT(Kernel, NotFound, 121);
|
||||
DEFINE_RESULT(Kernel, AlreadyExists, 122);
|
||||
DEFINE_RESULT(Kernel, ConnectionClosed, 123);
|
||||
DEFINE_RESULT(Kernel, UnhandledUserInterrupt, 124);
|
||||
DEFINE_RESULT(Kernel, InvalidState, 125);
|
||||
DEFINE_RESULT(Kernel, ReservedValue, 126);
|
||||
DEFINE_RESULT(Kernel, InvalidHwBreakpoint, 127);
|
||||
DEFINE_RESULT(Kernel, FatalUserException, 128);
|
||||
DEFINE_RESULT(Kernel, OwnedByAnotherProcess, 129);
|
||||
DEFINE_RESULT(Kernel, ConnectionRefused, 131);
|
||||
DEFINE_RESULT(Kernel, OutOfResource, 132);
|
||||
|
||||
DEFINE_RESULT(Kernel, IpcMapFailed, 259);
|
||||
DEFINE_RESULT(Kernel, IpcCmdbufTooSmall, 260);
|
||||
|
||||
DEFINE_RESULT(Kernel, NotDebugged, 520);
|
||||
|
||||
}
|
||||
118
mesosphere/include/mesosphere/core/make_object.hpp
Normal file
118
mesosphere/include/mesosphere/core/make_object.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/processes/KProcess.hpp>
|
||||
#include <mesosphere/kresources/KResourceLimit.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
auto MakeObjectRaw(Args&& ...args)
|
||||
{
|
||||
Result res = ResultSuccess();
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
auto reslimit = cctx.GetCurrentProcess()->GetResourceLimit();
|
||||
bool doReslimitCleanup = false;
|
||||
T *obj = nullptr;
|
||||
if constexpr (std::is_base_of_v<ILimitedResource<T>, T>) {
|
||||
if (reslimit != nullptr) {
|
||||
if (reslimit->Reserve(KResourceLimit::categoryOf<T>, 1, T::maxResourceAcqWaitTime)) {
|
||||
doReslimitCleanup = true;
|
||||
} else {
|
||||
return std::tuple<Result, T *>{ResultKernelOutOfResource(), nullptr};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj = new T;
|
||||
if (obj == nullptr) {
|
||||
res = ResultKernelResourceExhausted();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
res = obj->Initialize(std::forward<Args>(args)...);
|
||||
if (res.IsSuccess()) {
|
||||
doReslimitCleanup = false;
|
||||
if constexpr (std::is_base_of_v<ISetAllocated<T>, T>) {
|
||||
obj->AddToAllocatedSet();
|
||||
}
|
||||
} else {
|
||||
if constexpr (std::is_base_of_v<IClientServerParentTag, T>) {
|
||||
delete &obj->GetClient();
|
||||
delete &obj->GetServer();
|
||||
} else {
|
||||
delete obj;
|
||||
}
|
||||
|
||||
obj = nullptr;
|
||||
}
|
||||
cleanup:
|
||||
if (doReslimitCleanup) {
|
||||
reslimit->Release(KResourceLimit::categoryOf<T>, 1, 1);
|
||||
}
|
||||
|
||||
return std::tuple{res, obj};
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<!std::is_base_of_v<IClientServerParentTag, T>> * = nullptr, typename ...Args>
|
||||
auto MakeObject(Args&& ...args)
|
||||
{
|
||||
auto [res, obj] = MakeObjectRaw<T>(std::forward<Args>(args)...);
|
||||
return std::tuple<Result, SharedPtr<T>>{res, SharedPtr<T>{obj}};
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<std::is_base_of_v<IClientServerParentTag, T>> * = nullptr, typename ...Args>
|
||||
auto MakeObject(Args&& ...args)
|
||||
{
|
||||
// Bug in type inference?
|
||||
using RetType = std::tuple<Result, SharedPtr<typename T::ServerClass>, SharedPtr<typename T::ClientClass>>;
|
||||
auto [res, obj] = MakeObjectRaw<T>(std::forward<Args>(args)...);
|
||||
return res.IsSuccess() ? RetType{res, &obj->GetServer(), &obj->GetClient()} : RetType{res, nullptr, nullptr};
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<!std::is_base_of_v<IClientServerParentTag, T>> * = nullptr, typename ...Args>
|
||||
auto MakeObjectWithHandle(Args&& ...args)
|
||||
{
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
KProcess *currentProcess = cctx.GetCurrentProcess();
|
||||
KHandleTable &tbl = currentProcess->GetHandleTable();
|
||||
|
||||
auto [res, obj] = MakeObjectRaw<T>(std::forward<Args>(args)...);
|
||||
if (res.IsFailure()) {
|
||||
return std::tuple{res, Handle{}};
|
||||
}
|
||||
|
||||
return tbl.Generate(obj);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<std::is_base_of_v<IClientServerParentTag, T>> * = nullptr, typename ...Args>
|
||||
auto MakeObjectWithHandle(Args&& ...args)
|
||||
{
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
KProcess *currentProcess = cctx.GetCurrentProcess();
|
||||
KHandleTable &tbl = currentProcess->GetHandleTable();
|
||||
|
||||
auto [res, obj] = MakeObjectRaw<T>(std::forward<Args>(args)...);
|
||||
if (res.IsFailure()) {
|
||||
return std::tuple{res, Handle{}, Handle{}};
|
||||
}
|
||||
|
||||
auto [res2, serverHandle] = tbl.Generate(&obj->GetServer());
|
||||
if (res2.IsSuccess()) {
|
||||
auto [res3, clientHandle] = tbl.Generate(&obj->GetClient());
|
||||
if (res3.IsSuccess()) {
|
||||
return std::tuple{res3, serverHandle, clientHandle};
|
||||
} else {
|
||||
tbl.Close(serverHandle);
|
||||
return std::tuple{res3, Handle{}, Handle{}};
|
||||
}
|
||||
} else {
|
||||
return std::tuple{res2, Handle{}, Handle{}};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
48
mesosphere/include/mesosphere/core/types.hpp
Normal file
48
mesosphere/include/mesosphere/core/types.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
#include <chrono>
|
||||
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
||||
|
||||
#define MAX_CORES 4
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
using ushort = unsigned short;
|
||||
using uint = unsigned int;
|
||||
using ulong = unsigned long;
|
||||
|
||||
using std::size_t;
|
||||
|
||||
using uiptr = std::uintptr_t;
|
||||
using iptr = std::intptr_t;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
using s8 = int8_t;
|
||||
using s16 = int16_t;
|
||||
using s32 = int32_t;
|
||||
using s64 = int64_t;
|
||||
|
||||
using vu8 = volatile uint8_t;
|
||||
using vu16 = volatile uint16_t;
|
||||
using vu32 = volatile uint32_t;
|
||||
using vu64 = volatile uint64_t;
|
||||
|
||||
using vs8 = volatile int8_t;
|
||||
using vs16 = volatile int16_t;
|
||||
using vs32 = volatile int32_t;
|
||||
using vs64 = volatile int64_t;
|
||||
|
||||
template <typename T>
|
||||
using SharedPtr = boost::intrusive_ptr<T>;
|
||||
|
||||
}
|
||||
109
mesosphere/include/mesosphere/core/util.hpp
Normal file
109
mesosphere/include/mesosphere/core/util.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <array>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
/*
|
||||
Boost doesn't provide get_parent_from members for arrays so we have to implement this manually
|
||||
for arrays, for gcc at leadt.
|
||||
|
||||
Thanks fincs.
|
||||
*/
|
||||
|
||||
#define kassert(cond) ((void)(cond))
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename ClassT, typename MemberT>
|
||||
union __my_offsetof {
|
||||
const MemberT ClassT::* ptr;
|
||||
iptr offset;
|
||||
};
|
||||
|
||||
// Thanks neobrain
|
||||
template<typename T, size_t N, typename... Args, size_t... Indexes>
|
||||
static constexpr std::array<T, N> MakeArrayOfHelper(Args&&... args, std::index_sequence<Indexes...>) {
|
||||
// There are two parameter pack expansions here:
|
||||
// * The inner expansion is over "t"
|
||||
// * The outer expansion is over "Indexes"
|
||||
//
|
||||
// This function will always be called with sizeof...(Indexes) == N,
|
||||
// so the outer expansion generates exactly N copies of the constructor call
|
||||
return std::array<T, N> { ((void)Indexes, T { args... })... };
|
||||
}
|
||||
|
||||
// Thanks neobrain
|
||||
template<typename T, typename F, size_t N, typename... Args, size_t... Indexes>
|
||||
static constexpr std::array<T, N> MakeArrayWithFactorySequenceOfHelper(Args&&... args, std::index_sequence<Indexes...>) {
|
||||
return std::array<T, N> { T { F{}(std::integral_constant<size_t, Indexes>{}), args... }... };
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename ClassT, typename MemberT, size_t N>
|
||||
constexpr ClassT* GetParentFromArrayMember(MemberT* member, size_t index, const MemberT (ClassT::* ptr)[N]) noexcept {
|
||||
member -= index;
|
||||
return (ClassT*)((iptr)member - __my_offsetof<ClassT,MemberT[N]> { ptr }.offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ClassT, typename MemberT, size_t N>
|
||||
constexpr const ClassT* GetParentFromArrayMember(const MemberT* member, size_t index, const MemberT (ClassT::* ptr)[N]) noexcept {
|
||||
member -= index;
|
||||
return (const ClassT*)((iptr)member - __my_offsetof<ClassT,MemberT[N]> { ptr }.offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ClassT, typename MemberT>
|
||||
constexpr ClassT* GetParentFromMember(MemberT* member, const MemberT ClassT::* ptr) noexcept {
|
||||
return (ClassT*)((iptr)member - __my_offsetof<ClassT, MemberT> { ptr }.offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ClassT, typename MemberT>
|
||||
constexpr const ClassT* GetParentFromMember(const MemberT* member, const MemberT ClassT::* ptr) noexcept {
|
||||
return (const ClassT*)((iptr)member - __my_offsetof<ClassT, MemberT> { ptr }.offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T, size_t N, typename... Args>
|
||||
constexpr std::array<T, N> MakeArrayOf(Args&&... args) {
|
||||
return MakeArrayOfHelper<T, N, Args...>(std::forward<Args>(args)..., std::make_index_sequence<N>{});
|
||||
}
|
||||
|
||||
template<typename T, typename F, size_t N, typename... Args>
|
||||
constexpr std::array<T, N> MakeArrayWithFactorySequenceOf(Args&&... args) {
|
||||
return MakeArrayWithFactorySequenceOfHelper<T, F, N, Args...>(std::forward<Args>(args)..., std::make_index_sequence<N>{});
|
||||
}
|
||||
|
||||
/// Sequence of two distinc powers of 2
|
||||
constexpr ulong A038444(ulong n)
|
||||
{
|
||||
if (n == 0) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
ulong v = A038444(n - 1);
|
||||
ulong m1 = 1 << (63 - __builtin_clzl(v));
|
||||
ulong m2 = 1 << (63 - __builtin_clzl(v&~m1));
|
||||
|
||||
if (m2 << 1 == m1) {
|
||||
m2 = 1;
|
||||
m1 <<= 1;
|
||||
} else {
|
||||
m2 <<= 1;
|
||||
}
|
||||
|
||||
return m1 | m2;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
61
mesosphere/include/mesosphere/interfaces/IAlarmable.hpp
Normal file
61
mesosphere/include/mesosphere/interfaces/IAlarmable.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <mesosphere/board/KSystemClock.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct KAlarm;
|
||||
|
||||
struct AlarmableSetTag;
|
||||
|
||||
using AlarmableSetBaseHook = boost::intrusive::set_base_hook<
|
||||
boost::intrusive::tag<AlarmableSetTag>,
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>
|
||||
>;
|
||||
|
||||
class IAlarmable : public AlarmableSetBaseHook {
|
||||
public:
|
||||
struct Comparator {
|
||||
constexpr bool operator()(const IAlarmable &lhs, const IAlarmable &rhs) const {
|
||||
return lhs.alarmTime < rhs.alarmTime;
|
||||
}
|
||||
};
|
||||
|
||||
virtual void OnAlarm() = 0;
|
||||
|
||||
constexpr KSystemClock::time_point GetAlarmTime() const { return alarmTime; }
|
||||
|
||||
/// Precondition: alarm has not been set
|
||||
template<typename Clock, typename Duration>
|
||||
void SetAlarmTime(const std::chrono::time_point<Clock, Duration> &alarmTime)
|
||||
{
|
||||
SetAlarmTimeImpl(alarmTime);
|
||||
}
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
void SetAlarmIn(const std::chrono::duration<Rep, Period> &alarmTimeOffset)
|
||||
{
|
||||
SetAlarmTimeImpl(KSystemClock::now() + alarmTimeOffset);
|
||||
}
|
||||
|
||||
void ClearAlarm();
|
||||
|
||||
private:
|
||||
void SetAlarmTimeImpl(const KSystemClock::time_point &alarmTime);
|
||||
|
||||
KSystemClock::time_point alarmTime = KSystemClock::time_point{};
|
||||
|
||||
friend class KAlarm;
|
||||
};
|
||||
|
||||
|
||||
using AlarmableSetType =
|
||||
boost::intrusive::make_set<
|
||||
IAlarmable,
|
||||
boost::intrusive::base_hook<AlarmableSetBaseHook>,
|
||||
boost::intrusive::compare<IAlarmable::Comparator>
|
||||
>::type;
|
||||
|
||||
}
|
||||
31
mesosphere/include/mesosphere/interfaces/IClient.hpp
Normal file
31
mesosphere/include/mesosphere/interfaces/IClient.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <mesosphere/core/types.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
|
||||
#define MESOSPHERE_CLIENT_TRAITS(ParentId) using ParentClass = K##ParentId;
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct IClientTag {};
|
||||
|
||||
template<typename Parent, typename Client, typename Server>
|
||||
class IClient : public IClientTag {
|
||||
public:
|
||||
using ParentClass = Parent;
|
||||
using ClientClass = Client;
|
||||
using ServerClass = Server;
|
||||
|
||||
void *operator new(size_t sz) noexcept = delete;
|
||||
void operator delete(void *ptr) noexcept {}
|
||||
|
||||
ParentClass *GetParent() const { return parent.get(); }
|
||||
|
||||
protected:
|
||||
friend class IClientServerParent<ParentClass, ClientClass, ServerClass>;
|
||||
SharedPtr<ParentClass> parent{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
#define MESOSPHERE_CLIENT_SERVER_PARENT_TRAITS(ClientId, ServerId)\
|
||||
using ClientClass = K##ClientId;\
|
||||
using ServerClass = K##ServerId;
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct IClientServerParentTag {};
|
||||
|
||||
template<typename Parent, typename Client, typename Server>
|
||||
class IClientServerParent : public IClientServerParentTag {
|
||||
public:
|
||||
using ParentClass = Parent;
|
||||
using ClientClass = Client;
|
||||
using ServerClass = Server;
|
||||
|
||||
ClientClass &GetClient() { return client; }
|
||||
ServerClass &GetServer() { return server; }
|
||||
|
||||
protected:
|
||||
|
||||
void SetClientServerParent()
|
||||
{
|
||||
ParentClass *par = (ParentClass *)this;
|
||||
client.parent = par;
|
||||
server.parent = par;
|
||||
}
|
||||
|
||||
ClientClass client{};
|
||||
ServerClass server{};
|
||||
};
|
||||
|
||||
}
|
||||
16
mesosphere/include/mesosphere/interfaces/IInterruptible.hpp
Normal file
16
mesosphere/include/mesosphere/interfaces/IInterruptible.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class IWork;
|
||||
|
||||
class IInterruptible {
|
||||
public:
|
||||
|
||||
/// Top half in Linux jargon
|
||||
virtual IWork *HandleInterrupt(uint interruptId) = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/interfaces/IWork.hpp>
|
||||
#include <mesosphere/interfaces/IInterruptible.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class IInterruptibleWork : public IInterruptible, public IWork {
|
||||
public:
|
||||
|
||||
virtual IWork *HandleInterrupt(uint interruptId) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
// circular dep: #include "resource_limit.h"
|
||||
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <tuple>
|
||||
|
||||
#define MESOSPHERE_LIMITED_RESOURCE_TRAITS(maxTime) static constexpr auto maxResourceAcqWaitTime = maxTime;
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
void ReleaseResource(const SharedPtr<KProcess> &owner, KAutoObject::TypeId typeId, size_t count, size_t realCount);
|
||||
void ReleaseResource(const SharedPtr<KResourceLimit> &reslimit, KAutoObject::TypeId typeId, size_t count, size_t realCount);
|
||||
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
class ILimitedResource {
|
||||
public:
|
||||
|
||||
const SharedPtr<KProcess>& GetResourceOwner() const { return resourceOwner; }
|
||||
void SetResourceOwner(SharedPtr<KProcess> owner)
|
||||
{
|
||||
resourceOwner = std::move(owner);
|
||||
isLimitedResourceActive = true;
|
||||
}
|
||||
|
||||
virtual std::tuple<size_t, size_t> GetResourceCount()
|
||||
{
|
||||
return {1, 1}; // current, real
|
||||
}
|
||||
|
||||
~ILimitedResource()
|
||||
{
|
||||
if (isLimitedResourceActive) {
|
||||
auto [cur, real] = GetResourceCount();
|
||||
detail::ReleaseResource(resourceOwner, Derived::typeId, cur, real);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SharedPtr<KProcess> resourceOwner{};
|
||||
bool isLimitedResourceActive = false;
|
||||
};
|
||||
|
||||
}
|
||||
31
mesosphere/include/mesosphere/interfaces/IServer.hpp
Normal file
31
mesosphere/include/mesosphere/interfaces/IServer.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <mesosphere/core/types.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
|
||||
#define MESOSPHERE_SERVER_TRAITS(ParentId) using ParentClass = K##ParentId;
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct IServerTag {};
|
||||
|
||||
template<typename Parent, typename Client, typename Server>
|
||||
class IServer : public IServerTag {
|
||||
public:
|
||||
using ParentClass = Parent;
|
||||
using ClientClass = Client;
|
||||
using ServerClass = Server;
|
||||
|
||||
void *operator new(size_t sz) noexcept = delete;
|
||||
void operator delete(void *ptr) noexcept {};
|
||||
|
||||
ParentClass *GetParent() const { return parent.get(); }
|
||||
|
||||
protected:
|
||||
friend class IClientServerParent<ParentClass, ClientClass, ServerClass>;
|
||||
SharedPtr<ParentClass> parent{};
|
||||
};
|
||||
|
||||
}
|
||||
60
mesosphere/include/mesosphere/interfaces/ISetAllocated.hpp
Normal file
60
mesosphere/include/mesosphere/interfaces/ISetAllocated.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/kresources/KObjectAllocator.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename Derived>
|
||||
class ISetAllocated : public KObjectAllocator<Derived>::AllocatedSetHookType
|
||||
{
|
||||
public:
|
||||
static void InitializeAllocator(void *buffer, size_t capacity) noexcept
|
||||
{
|
||||
allocator.GetSlabHeap().initialize(buffer, capacity);
|
||||
}
|
||||
|
||||
void *operator new(size_t sz) noexcept
|
||||
{
|
||||
kassert(sz == sizeof(Derived));
|
||||
return allocator.GetSlabHeap().allocate();
|
||||
}
|
||||
|
||||
void operator delete(void *ptr) noexcept
|
||||
{
|
||||
allocator.GetSlabHeap().deallocate((Derived *)ptr);
|
||||
}
|
||||
|
||||
void AddToAllocatedSet() noexcept
|
||||
{
|
||||
Derived *d = (Derived *)this;
|
||||
allocator.RegisterObject(*d);
|
||||
isRegisteredToAllocator = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
void RemoveFromAllocatedSet() noexcept
|
||||
{
|
||||
Derived *d = (Derived *)this;
|
||||
allocator.UnregisterObject(*d);
|
||||
}
|
||||
|
||||
virtual ~ISetAllocated()
|
||||
{
|
||||
if (isRegisteredToAllocator) {
|
||||
RemoveFromAllocatedSet();
|
||||
isRegisteredToAllocator = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool isRegisteredToAllocator = false;
|
||||
|
||||
protected:
|
||||
static KObjectAllocator<Derived> allocator;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
KObjectAllocator<Derived> ISetAllocated<Derived>::allocator{};
|
||||
|
||||
}
|
||||
35
mesosphere/include/mesosphere/interfaces/ISlabAllocated.hpp
Normal file
35
mesosphere/include/mesosphere/interfaces/ISlabAllocated.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/kresources/KSlabHeap.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename Derived>
|
||||
class ISlabAllocated
|
||||
{
|
||||
public:
|
||||
static void InitializeSlabHeap(void *buffer, size_t capacity) noexcept
|
||||
{
|
||||
slabHeap.initialize(buffer, capacity);
|
||||
}
|
||||
|
||||
void *operator new(size_t sz) noexcept
|
||||
{
|
||||
kassert(sz == sizeof(Derived));
|
||||
return slabHeap.allocate();
|
||||
}
|
||||
|
||||
void operator delete(void *ptr) noexcept
|
||||
{
|
||||
slabHeap.deallocate((Derived *)ptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
static KSlabHeap<Derived> slabHeap;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
KSlabHeap<Derived> ISlabAllocated<Derived>::slabHeap{};
|
||||
|
||||
}
|
||||
29
mesosphere/include/mesosphere/interfaces/IWork.hpp
Normal file
29
mesosphere/include/mesosphere/interfaces/IWork.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <boost/intrusive/slist.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct WorkSListTag;
|
||||
|
||||
using WorkSListBaseHook = boost::intrusive::slist_base_hook<
|
||||
boost::intrusive::tag<WorkSListTag>,
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>
|
||||
>;
|
||||
|
||||
/// Bottom half in Linux jargon
|
||||
class IWork : public WorkSListBaseHook {
|
||||
public:
|
||||
virtual void DoWork() = 0;
|
||||
};
|
||||
|
||||
using WorkSList = boost::intrusive::make_slist<
|
||||
IWork,
|
||||
boost::intrusive::base_hook<WorkSListBaseHook>,
|
||||
boost::intrusive::cache_last<true>,
|
||||
boost::intrusive::constant_time_size<false>
|
||||
>::type;
|
||||
|
||||
}
|
||||
34
mesosphere/include/mesosphere/interrupts/KAlarm.hpp
Normal file
34
mesosphere/include/mesosphere/interrupts/KAlarm.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/interfaces/IInterruptibleWork.hpp>
|
||||
#include <mesosphere/interfaces/IAlarmable.hpp>
|
||||
#include <mesosphere/interrupts/KInterruptSpinLock.hpp>
|
||||
#include <mesosphere/board/KSystemClock.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KAlarm final : public IInterruptibleWork {
|
||||
public:
|
||||
|
||||
//KAlarm() = default;
|
||||
|
||||
/// Precondition: alarmable not already added
|
||||
void AddAlarmable(IAlarmable &alarmable);
|
||||
|
||||
/// Precondition: alarmable is present
|
||||
void RemoveAlarmable(const IAlarmable &alarmable);
|
||||
|
||||
void HandleAlarm();
|
||||
|
||||
KAlarm(const KAlarm &) = delete;
|
||||
KAlarm(KAlarm &&) = delete;
|
||||
KAlarm &operator=(const KAlarm &) = delete;
|
||||
KAlarm &operator=(KAlarm &&) = delete;
|
||||
|
||||
private:
|
||||
mutable KInterruptSpinLock<false> spinlock{};
|
||||
AlarmableSetType alarmables{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KThread;
|
||||
inline void IncrementThreadInterruptBottomHalfLockCount(KThread &thread);
|
||||
inline void DecrementThreadInterruptBottomHalfLockCount(KThread &thread);
|
||||
|
||||
class KInterruptBottomHalfGuard final {
|
||||
public:
|
||||
KInterruptBottomHalfGuard()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
IncrementInterruptBottomHalfLockCount(*curThread);
|
||||
}
|
||||
|
||||
~KInterruptBottomHalfGuard()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
DecrementInterruptBottomHalfLockCount(*curThread);
|
||||
}
|
||||
|
||||
KInterruptBottomHalfGuard(const KInterruptBottomHalfGuard &) = delete;
|
||||
KInterruptBottomHalfGuard(KInterruptBottomHalfGuard &&) = delete;
|
||||
KInterruptBottomHalfGuard &operator=(const KInterruptBottomHalfGuard &) = delete;
|
||||
KInterruptBottomHalfGuard &operator=(KInterruptBottomHalfGuard &&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
34
mesosphere/include/mesosphere/interrupts/KInterruptEvent.hpp
Normal file
34
mesosphere/include/mesosphere/interrupts/KInterruptEvent.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/processes/KReadableEvent.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KInterruptEvent final : public KReadableEvent, public ISetAllocated<KInterruptEvent> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(ReadableEvent, InterruptEvent);
|
||||
|
||||
void *operator new(size_t sz) noexcept { return ISetAllocated<KInterruptEvent>::operator new(sz); }
|
||||
void operator delete(void *ptr) noexcept { ISetAllocated<KInterruptEvent>::operator delete(ptr); }
|
||||
|
||||
virtual ~KInterruptEvent();
|
||||
Result Initialize(int irqId, u32 flags);
|
||||
|
||||
private:
|
||||
// TODO: receiver
|
||||
int irqId = -1;
|
||||
};
|
||||
|
||||
inline void intrusive_ptr_add_ref(KInterruptEvent *obj)
|
||||
{
|
||||
intrusive_ptr_add_ref((KAutoObject *)obj);
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_release(KInterruptEvent *obj)
|
||||
{
|
||||
intrusive_ptr_release((KAutoObject *)obj);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/arch/KInterruptMaskGuard.hpp>
|
||||
#include <mesosphere/arch/KSpinLock.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KThread;
|
||||
inline void IncrementThreadInterruptBottomHalfLockCount(KThread &thread);
|
||||
inline void DecrementThreadInterruptBottomHalfLockCount(KThread &thread);
|
||||
|
||||
template<bool disableInterrupts = false>
|
||||
class KInterruptSpinLock : public KSpinLock {
|
||||
public:
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
IncrementThreadInterruptBottomHalfLockCount(*curThread);
|
||||
if (!KSpinLock::try_lock()) {
|
||||
DecrementThreadInterruptBottomHalfLockCount(*curThread);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
IncrementThreadInterruptBottomHalfLockCount(*curThread);
|
||||
KSpinLock::lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
KSpinLock::unlock();
|
||||
DecrementThreadInterruptBottomHalfLockCount(*curThread);
|
||||
}
|
||||
|
||||
KInterruptSpinLock() = default;
|
||||
KInterruptSpinLock(const KInterruptSpinLock &) = delete;
|
||||
KInterruptSpinLock(KInterruptSpinLock &&) = delete;
|
||||
KInterruptSpinLock &operator=(const KInterruptSpinLock &) = delete;
|
||||
KInterruptSpinLock &operator=(KInterruptSpinLock &&) = delete;
|
||||
};
|
||||
|
||||
template<>
|
||||
class KInterruptSpinLock<true> : public KSpinLock {
|
||||
public:
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
flags = MaskInterrupts();
|
||||
if (!KSpinLock::try_lock()) {
|
||||
RestoreInterrupts(flags);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
flags = MaskInterrupts();
|
||||
KSpinLock::lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
KSpinLock::unlock();
|
||||
RestoreInterrupts(flags);
|
||||
}
|
||||
|
||||
KInterruptSpinLock() = default;
|
||||
KInterruptSpinLock(const KInterruptSpinLock &) = delete;
|
||||
KInterruptSpinLock(KInterruptSpinLock &&) = delete;
|
||||
KInterruptSpinLock &operator=(const KInterruptSpinLock &) = delete;
|
||||
KInterruptSpinLock &operator=(KInterruptSpinLock &&) = delete;
|
||||
|
||||
private:
|
||||
InterruptFlagsType flags = 0;
|
||||
};
|
||||
|
||||
}
|
||||
27
mesosphere/include/mesosphere/interrupts/KWorkQueue.hpp
Normal file
27
mesosphere/include/mesosphere/interrupts/KWorkQueue.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/interfaces/IWork.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KWorkQueue final {
|
||||
public:
|
||||
|
||||
void AddWork(IWork &work);
|
||||
void Initialize();
|
||||
|
||||
void HandleWorkQueue();
|
||||
|
||||
KWorkQueue(const KWorkQueue &) = delete;
|
||||
KWorkQueue(KWorkQueue &&) = delete;
|
||||
KWorkQueue &operator=(const KWorkQueue &) = delete;
|
||||
KWorkQueue &operator=(KWorkQueue &&) = delete;
|
||||
|
||||
private:
|
||||
WorkSList workQueue{};
|
||||
SharedPtr<KThread> handlerThread{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/kresources/KSlabHeap.hpp>
|
||||
#include <mesosphere/threading/KMutex.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class KObjectAllocator {
|
||||
private:
|
||||
struct Comparator {
|
||||
constexpr bool operator()(const T &lhs, const T &rhs) const
|
||||
{
|
||||
return lhs.GetComparisonKey() < rhs.GetComparisonKey();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ComparatorEqual {
|
||||
constexpr u64 operator()(const T &val) const
|
||||
{
|
||||
return val.GetComparisonKey();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
struct HookTag;
|
||||
|
||||
using AllocatedSetHookType = boost::intrusive::set_base_hook<
|
||||
boost::intrusive::tag<HookTag>,
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>
|
||||
>;
|
||||
using AllocatedSetType = typename
|
||||
boost::intrusive::make_set<
|
||||
T,
|
||||
boost::intrusive::base_hook<AllocatedSetHookType>,
|
||||
boost::intrusive::compare<Comparator>
|
||||
>::type;
|
||||
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using void_pointer = void *;
|
||||
using const_void_ptr = const void *;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
AllocatedSetType &GetAllocatedSet()
|
||||
{
|
||||
return allocatedSet;
|
||||
}
|
||||
|
||||
KSlabHeap<T> &GetSlabHeap()
|
||||
{
|
||||
return slabHeap;
|
||||
}
|
||||
|
||||
void RegisterObject(T &obj) noexcept
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
allocatedSet.insert(obj);
|
||||
}
|
||||
|
||||
void UnregisterObject(T &obj) noexcept
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
allocatedSet.erase(obj);
|
||||
}
|
||||
|
||||
SharedPtr<T> FindObject(u64 comparisonKey) noexcept
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
auto it = allocatedSet.find(comparisonKey, ComparatorEqual{});
|
||||
return it != allocatedSet.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
AllocatedSetType allocatedSet{};
|
||||
KSlabHeap<T> slabHeap{};
|
||||
mutable KMutex mutex{};
|
||||
};
|
||||
|
||||
}
|
||||
74
mesosphere/include/mesosphere/kresources/KResourceLimit.hpp
Normal file
74
mesosphere/include/mesosphere/kresources/KResourceLimit.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/threading/KConditionVariable.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KTransferMemory;
|
||||
|
||||
class KResourceLimit final :
|
||||
public KAutoObject,
|
||||
public ISetAllocated<KResourceLimit>
|
||||
{
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, ResourceLimit);
|
||||
|
||||
enum class Category : uint {
|
||||
Memory = 0,
|
||||
Threads,
|
||||
Events,
|
||||
TransferMemories,
|
||||
Sessions,
|
||||
|
||||
Max,
|
||||
};
|
||||
|
||||
static constexpr Category GetCategory(KAutoObject::TypeId typeId) {
|
||||
switch (typeId) {
|
||||
case KAutoObject::TypeId::Thread: return Category::Threads;
|
||||
case KAutoObject::TypeId::Event: return Category::Events;
|
||||
case KAutoObject::TypeId::TransferMemory: return Category::TransferMemories;
|
||||
case KAutoObject::TypeId::Session: return Category::Sessions;
|
||||
case KAutoObject::TypeId::LightSession: return Category::Sessions;
|
||||
default: return Category::Max;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> static constexpr Category categoryOf = GetCategory(T::typeId);
|
||||
|
||||
static KResourceLimit &GetDefaultInstance() { return defaultInstance; }
|
||||
|
||||
size_t GetCurrentValue(Category category) const;
|
||||
size_t GetLimitValue(Category category) const;
|
||||
size_t GetRemainingValue(Category category) const;
|
||||
|
||||
bool SetLimitValue(Category category, size_t value);
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
bool Reserve(Category category, size_t count, const std::chrono::duration<Rep, Period>& timeout)
|
||||
{
|
||||
return ReserveDetail(category, count, KSystemClock::now() + timeout);
|
||||
}
|
||||
|
||||
void Release(Category category, size_t count, size_t realCount);
|
||||
|
||||
private:
|
||||
|
||||
static KResourceLimit defaultInstance;
|
||||
bool ReserveDetail(Category category, size_t count, const KSystemClock::time_point &timeoutTime);
|
||||
|
||||
// Signed in official kernel
|
||||
size_t limitValues[(size_t)Category::Max] = {};
|
||||
|
||||
// Current value: real value + dangling resources about to be released
|
||||
size_t currentValues[(size_t)Category::Max] = {};
|
||||
size_t realValues[(size_t)Category::Max] = {};
|
||||
|
||||
mutable KConditionVariable condvar{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(ResourceLimit);
|
||||
|
||||
}
|
||||
55
mesosphere/include/mesosphere/kresources/KSlabHeap.hpp
Normal file
55
mesosphere/include/mesosphere/kresources/KSlabHeap.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/kresources/KSlabStack.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class KSlabHeap {
|
||||
public:
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using void_pointer = void *;
|
||||
using const_void_ptr = const void *;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
private:
|
||||
KSlabStack<T> stack{};
|
||||
size_t capacity = 0;
|
||||
T *bufferStart = nullptr;
|
||||
|
||||
public:
|
||||
T *allocate() noexcept
|
||||
{
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
void deallocate(T *elem) noexcept
|
||||
{
|
||||
kassert(elem >= bufferStart && elem < bufferStart + capacity);
|
||||
stack.push(elem);
|
||||
}
|
||||
|
||||
constexpr size_t size() const
|
||||
{
|
||||
return capacity;
|
||||
}
|
||||
|
||||
KSlabHeap() noexcept = default;
|
||||
|
||||
void initialize(void *buffer, size_t capacity)
|
||||
{
|
||||
this->capacity = capacity;
|
||||
this->bufferStart = (T *)buffer;
|
||||
stack.initialize(buffer, capacity);
|
||||
}
|
||||
|
||||
KSlabHeap(void *buffer, size_t capacity) noexcept : stack(buffer, capacity), capacity(capacity), bufferStart((T *)buffer)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
78
mesosphere/include/mesosphere/kresources/KSlabStack.hpp
Normal file
78
mesosphere/include/mesosphere/kresources/KSlabStack.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <atomic>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class KSlabStack {
|
||||
public:
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using void_pointer = void *;
|
||||
using const_void_ptr = const void *;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
Node *next;
|
||||
};
|
||||
|
||||
std::atomic<Node *> head;
|
||||
public:
|
||||
|
||||
void push(T *data) noexcept
|
||||
{
|
||||
Node *newHead = (Node *)data;
|
||||
Node *oldHead = head.load();
|
||||
do {
|
||||
newHead->next = oldHead;
|
||||
} while(!head.compare_exchange_weak(oldHead, newHead));
|
||||
}
|
||||
|
||||
T *pop() noexcept
|
||||
{
|
||||
Node *newHead;
|
||||
Node *oldHead = head.load();
|
||||
if (oldHead == nullptr) {
|
||||
return nullptr;
|
||||
} else {
|
||||
do {
|
||||
newHead = oldHead == nullptr ? oldHead : oldHead->next;
|
||||
} while(!head.compare_exchange_weak(oldHead, newHead));
|
||||
|
||||
return (T *)oldHead;
|
||||
}
|
||||
}
|
||||
|
||||
KSlabStack() noexcept = default;
|
||||
|
||||
// Not reentrant (unlike NN's init function)
|
||||
void initialize(void *buffer, size_t size) noexcept
|
||||
{
|
||||
T *ar = (T *)buffer;
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node *ndlast = (Node *)&ar[size - 1];
|
||||
ndlast->next = nullptr;
|
||||
|
||||
for (size_t i = 0; i < size - 1; i++) {
|
||||
Node *nd = (Node *)&ar[i];
|
||||
Node *ndnext = (Node *)&ar[i + 1];
|
||||
nd->next = ndnext;
|
||||
}
|
||||
|
||||
Node *ndfirst = (Node *)&ar[0];
|
||||
head.store(ndfirst);
|
||||
}
|
||||
|
||||
KSlabStack(void *buffer, size_t size) { initialize(buffer, size); }
|
||||
};
|
||||
|
||||
}
|
||||
38
mesosphere/include/mesosphere/processes/KClientPort.hpp
Normal file
38
mesosphere/include/mesosphere/processes/KClientPort.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/interfaces/IClient.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <tuple>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KPort;
|
||||
class KServerPort;
|
||||
|
||||
class KClientPort final :
|
||||
public KSynchronizationObject,
|
||||
public IClient<KPort, KClientPort, KServerPort> {
|
||||
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(SynchronizationObject, ClientPort);
|
||||
|
||||
virtual ~KClientPort();
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
std::tuple<Result, SharedPtr<KLightClientSession>> ConnectLight();
|
||||
|
||||
private:
|
||||
friend class KPort;
|
||||
|
||||
std::atomic<int> numSessions{0};
|
||||
std::atomic<int> peakNumNormalSessions{0};
|
||||
int maxSessions = 0;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(ClientPort);
|
||||
|
||||
}
|
||||
34
mesosphere/include/mesosphere/processes/KEvent.hpp
Normal file
34
mesosphere/include/mesosphere/processes/KEvent.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/interfaces/ILimitedResource.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
#include <mesosphere/processes/KReadableEvent.hpp>
|
||||
#include <mesosphere/processes/KWritableEvent.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KEvent final :
|
||||
public KAutoObject,
|
||||
public IClientServerParent<KEvent, KReadableEvent, KWritableEvent>,
|
||||
public ISetAllocated<KEvent>,
|
||||
public ILimitedResource<KEvent> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, Event);
|
||||
MESOSPHERE_CLIENT_SERVER_PARENT_TRAITS(ReadableEvent, WritableEvent);
|
||||
MESOSPHERE_LIMITED_RESOURCE_TRAITS(10s);
|
||||
|
||||
virtual ~KEvent();
|
||||
|
||||
Result Initialize();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(Event);
|
||||
|
||||
}
|
||||
78
mesosphere/include/mesosphere/processes/KHandleTable.hpp
Normal file
78
mesosphere/include/mesosphere/processes/KHandleTable.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Handle.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interrupts/KInterruptSpinLock.hpp>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KThread;
|
||||
class KProcess;
|
||||
|
||||
class KHandleTable final {
|
||||
public:
|
||||
|
||||
static constexpr size_t capacityLimit = 1024;
|
||||
static constexpr Handle selfThreadAlias{0, -1, true};
|
||||
static constexpr Handle selfProcessAlias{1, -1, true};
|
||||
|
||||
template<typename T>
|
||||
SharedPtr<T> Get(Handle handle, bool allowAlias = true) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, KAutoObject>) {
|
||||
(void)allowAlias;
|
||||
return GetAutoObject(handle);
|
||||
} else if constexpr (std::is_same_v<T, KThread>) {
|
||||
return GetThread(handle, allowAlias);
|
||||
} else if constexpr (std::is_same_v<T, KProcess>) {
|
||||
return GetProcess(handle, allowAlias);
|
||||
} else {
|
||||
return DynamicObjectCast<T>(GetAutoObject(handle));
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<Result, Handle> Generate(SharedPtr<KAutoObject> obj);
|
||||
|
||||
/// For deferred-init
|
||||
Result Set(SharedPtr<KAutoObject> obj, Handle handle);
|
||||
|
||||
bool Close(Handle handle);
|
||||
void Destroy();
|
||||
|
||||
constexpr size_t GetNumActive() const { return numActive; }
|
||||
constexpr size_t GetSize() const { return size; }
|
||||
constexpr size_t GetCapacity() const { return capacity; }
|
||||
|
||||
Result Initialize(size_t capacity); // TODO: implement!
|
||||
|
||||
~KHandleTable();
|
||||
|
||||
private:
|
||||
|
||||
bool IsValid(Handle handle) const;
|
||||
SharedPtr<KAutoObject> GetAutoObject(Handle handle) const;
|
||||
SharedPtr<KThread> GetThread(Handle handle, bool allowAlias = true) const;
|
||||
SharedPtr<KProcess> GetProcess(Handle handle, bool allowAlias = true) const;
|
||||
|
||||
struct Entry {
|
||||
SharedPtr<KAutoObject> object{};
|
||||
s16 id = 0;
|
||||
};
|
||||
|
||||
std::array<Entry, capacityLimit> entries{};
|
||||
|
||||
// Here the official kernel uses pointer, Yuzu and ourselves are repurposing a field in Entry instead.
|
||||
s16 firstFreeIndex = 0;
|
||||
s16 idCounter = 1;
|
||||
|
||||
u16 numActive = 0, size = 0, capacity = 0;
|
||||
|
||||
mutable KInterruptSpinLock<false> spinlock;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interfaces/IClient.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KLightSession;
|
||||
class KClientPort;
|
||||
|
||||
struct LightSessionRequest;
|
||||
|
||||
class KLightClientSession final : public KAutoObject, public IClient<KLightSession, KLightClientSession, KLightServerSession> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, LightClientSession);
|
||||
|
||||
virtual ~KLightClientSession();
|
||||
|
||||
Result SendSyncRequest(LightSessionRequest *request);
|
||||
|
||||
private:
|
||||
friend class KLightSession;
|
||||
|
||||
SharedPtr<KClientPort> parentClientPort = nullptr;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(LightClientSession);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interfaces/IServer.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KLightClientSession;
|
||||
class KLightSession;
|
||||
class KClientPort;
|
||||
|
||||
struct LightSessionRequest;
|
||||
|
||||
struct LightServerSessionListTag;
|
||||
using LightServerSessionListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::tag<LightServerSessionListTag> >;
|
||||
|
||||
class KLightServerSession final :
|
||||
public KAutoObject,
|
||||
public IServer<KLightSession, KLightClientSession, KLightServerSession>,
|
||||
public LightServerSessionListBaseHook {
|
||||
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, LightServerSession);
|
||||
|
||||
using List = typename boost::intrusive::make_list<
|
||||
KLightServerSession,
|
||||
boost::intrusive::base_hook<LightServerSessionListBaseHook>,
|
||||
boost::intrusive::constant_time_size<false>
|
||||
>::type;
|
||||
|
||||
virtual ~KLightServerSession();
|
||||
|
||||
/// Needs to be called from critical section
|
||||
Result HandleSyncRequest(KThread &sender);
|
||||
|
||||
Result ReplyAndReceive(LightSessionRequest *request);
|
||||
private:
|
||||
friend class KLightSession;
|
||||
|
||||
void Terminate(bool fromServer);
|
||||
|
||||
KThread::WaitList senderThreads{}, receiverThreads{};
|
||||
SharedPtr<KThread> currentSender{};
|
||||
KThread *currentReceiver = nullptr;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(LightServerSession);
|
||||
|
||||
}
|
||||
46
mesosphere/include/mesosphere/processes/KLightSession.hpp
Normal file
46
mesosphere/include/mesosphere/processes/KLightSession.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/interfaces/ILimitedResource.hpp>
|
||||
#include <mesosphere/processes/KLightClientSession.hpp>
|
||||
#include <mesosphere/processes/KLightServerSession.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KPort;
|
||||
|
||||
struct LightSessionRequest {
|
||||
s32 cmdId;
|
||||
u32 data[6];
|
||||
};
|
||||
|
||||
class KLightSession final :
|
||||
public KAutoObject,
|
||||
public ISetAllocated<KLightSession>,
|
||||
public ILimitedResource<KLightSession>,
|
||||
public IClientServerParent<KLightSession, KLightClientSession, KLightServerSession> {
|
||||
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, LightSession);
|
||||
MESOSPHERE_LIMITED_RESOURCE_TRAITS(10s);
|
||||
|
||||
virtual ~KLightSession();
|
||||
|
||||
Result Initialize(KPort *parentPort = nullptr);
|
||||
|
||||
private:
|
||||
friend class KLightClientSession;
|
||||
friend class KLightServerSession;
|
||||
|
||||
void Terminate(bool fromServer);
|
||||
bool isClientAlive = false;
|
||||
bool isServerAlive = false;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(LightSession);
|
||||
|
||||
}
|
||||
39
mesosphere/include/mesosphere/processes/KPort.hpp
Normal file
39
mesosphere/include/mesosphere/processes/KPort.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/processes/KClientPort.hpp>
|
||||
#include <mesosphere/processes/KServerPort.hpp>
|
||||
#include <mesosphere/processes/KLightSession.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KPort final :
|
||||
public KAutoObject,
|
||||
public ISetAllocated<KPort>,
|
||||
public IClientServerParent<KPort, KClientPort, KServerPort> {
|
||||
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, Port);
|
||||
|
||||
virtual ~KPort();
|
||||
|
||||
Result Initialize(int maxSessions, bool isLight);
|
||||
|
||||
Result AddLightServerSession(KLightServerSession &lightServerSession);
|
||||
|
||||
private:
|
||||
friend class KClientPort;
|
||||
friend class KServerPort;
|
||||
|
||||
bool isClientAlive = false;
|
||||
bool isServerAlive = false;
|
||||
bool isLight = false;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(Port);
|
||||
|
||||
}
|
||||
79
mesosphere/include/mesosphere/processes/KProcess.hpp
Normal file
79
mesosphere/include/mesosphere/processes/KProcess.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
class KThread;
|
||||
class KResourceLimit;
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/processes/KHandleTable.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KProcess final : public KSynchronizationObject /* FIXME */ {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(SynchronizationObject, Process);
|
||||
|
||||
enum class State : uint {
|
||||
Created = 0,
|
||||
CreatedAttached,
|
||||
Started,
|
||||
Crashed,
|
||||
StartedAttached,
|
||||
Exiting,
|
||||
Exited,
|
||||
DebugSuspended,
|
||||
};
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
constexpr long GetSchedulerOperationCount() const { return schedulerOperationCount; }
|
||||
|
||||
void IncrementSchedulerOperationCount() { ++schedulerOperationCount; }
|
||||
void SetLastThreadAndIdleSelectionCount(KThread *thread, ulong idleSelectionCount);
|
||||
|
||||
const SharedPtr<KResourceLimit> &GetResourceLimit() const { return reslimit; }
|
||||
|
||||
KHandleTable &GetHandleTable() { return handleTable; }
|
||||
|
||||
constexpr State GetState() const { return state; }
|
||||
|
||||
void SetDebugPauseState(bool pause);
|
||||
|
||||
KDebug *GetDebug() const { return debug; }
|
||||
void SetDebug(KDebug *debug);
|
||||
void ClearDebug(State attachState);
|
||||
|
||||
template<typename F, typename ...Args>
|
||||
void ForEachThread(F f, Args &&...args)
|
||||
{
|
||||
std::scoped_lock s{mutex};
|
||||
std::scoped_lock s2{threadingMutex};
|
||||
|
||||
for (KThread &t : threadList) {
|
||||
f(t, std::forward(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KThread::ProcessList threadList{};
|
||||
|
||||
KThread *lastThreads[MAX_CORES]{nullptr};
|
||||
ulong lastIdleSelectionCount[MAX_CORES]{0};
|
||||
long schedulerOperationCount = -1;
|
||||
|
||||
State state = State::Created;
|
||||
bool stateChanged = false;
|
||||
|
||||
KDebug *debug = nullptr;
|
||||
SharedPtr<KResourceLimit> reslimit{};
|
||||
KHandleTable handleTable{};
|
||||
|
||||
mutable KMutex mutex{}, threadingMutex{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(Process);
|
||||
|
||||
}
|
||||
34
mesosphere/include/mesosphere/processes/KReadableEvent.hpp
Normal file
34
mesosphere/include/mesosphere/processes/KReadableEvent.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/interfaces/IClient.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KWritableEvent;
|
||||
class KEvent;
|
||||
|
||||
// Inherited by KInterruptEvent
|
||||
class KReadableEvent : public KSynchronizationObject, public IClient<KEvent, KReadableEvent, KWritableEvent> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(SynchronizationObject, ReadableEvent);
|
||||
MESOSPHERE_CLIENT_TRAITS(Event);
|
||||
|
||||
virtual ~KReadableEvent();
|
||||
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
Result Reset();
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
private:
|
||||
bool isSignaled = false;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(ReadableEvent);
|
||||
|
||||
}
|
||||
33
mesosphere/include/mesosphere/processes/KServerPort.hpp
Normal file
33
mesosphere/include/mesosphere/processes/KServerPort.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/processes/KLightServerSession.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KPort;
|
||||
class KClientPort;
|
||||
|
||||
class KServerPort final :
|
||||
public KSynchronizationObject,
|
||||
public IServer<KPort, KClientPort, KServerPort> {
|
||||
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(SynchronizationObject, ServerPort);
|
||||
|
||||
virtual ~KServerPort();
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
private:
|
||||
friend class KPort;
|
||||
Result AddLightServerSession(KLightServerSession &lightServerSession);
|
||||
|
||||
KLightServerSession::List lightServerSessions{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(ServerPort);
|
||||
|
||||
}
|
||||
33
mesosphere/include/mesosphere/processes/KWritableEvent.hpp
Normal file
33
mesosphere/include/mesosphere/processes/KWritableEvent.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/interfaces/IServer.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KReadableEvent;
|
||||
class KEvent;
|
||||
|
||||
class KWritableEvent final : public KAutoObject, public IServer<KEvent, KReadableEvent, KWritableEvent> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, WritableEvent);
|
||||
MESOSPHERE_SERVER_TRAITS(Event);
|
||||
|
||||
virtual ~KWritableEvent();
|
||||
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
|
||||
private:
|
||||
friend class KEvent;
|
||||
|
||||
SharedPtr<KReadableEvent> client{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(WritableEvent);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/threading/KMutex.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
|
||||
/// Provides an interface similar to std::condition_variable
|
||||
class KConditionVariable final {
|
||||
public:
|
||||
|
||||
using native_handle_type = uiptr;
|
||||
|
||||
KConditionVariable() = default;
|
||||
KConditionVariable(const KConditionVariable &) = delete;
|
||||
KConditionVariable(KConditionVariable &&) = delete;
|
||||
KConditionVariable &operator=(const KConditionVariable &) = delete;
|
||||
KConditionVariable &operator=(KConditionVariable &&) = delete;
|
||||
|
||||
native_handle_type native_handle() { return mutex_.native_handle(); }
|
||||
|
||||
KMutex &mutex() { return mutex_; }
|
||||
|
||||
void wait() noexcept
|
||||
{
|
||||
wait_until_impl(KSystemClock::never);
|
||||
}
|
||||
template<typename Predicate>
|
||||
void wait(Predicate pred)
|
||||
{
|
||||
while (!pred()) {
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Clock, typename Duration>
|
||||
void wait_until(const std::chrono::time_point<Clock, Duration> &timeoutPoint) noexcept
|
||||
{
|
||||
wait_until_impl(timeoutPoint);
|
||||
}
|
||||
template<typename Clock, typename Duration, typename Predicate>
|
||||
bool wait_until(const std::chrono::time_point<Clock, Duration> &timeoutPoint, Predicate pred)
|
||||
{
|
||||
while (!pred()) {
|
||||
wait_until(timeoutPoint);
|
||||
if (Clock::now() >= timeoutPoint) {
|
||||
return pred();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
void wait_for(const std::chrono::duration<Rep, Period>& timeout) noexcept
|
||||
{
|
||||
wait_until(KSystemClock::now() + timeout);
|
||||
}
|
||||
|
||||
template<typename Rep, typename Period, typename Predicate>
|
||||
bool wait_for(const std::chrono::duration<Rep, Period>& timeout, Predicate pred)
|
||||
{
|
||||
return wait_until(KSystemClock::now() + timeout, std::move(pred));
|
||||
}
|
||||
|
||||
void notify_one() noexcept;
|
||||
void notify_all() noexcept;
|
||||
|
||||
private:
|
||||
void wait_until_impl(const KSystemClock::time_point &timeoutPoint) noexcept;
|
||||
|
||||
KMutex mutex_{};
|
||||
KThread::WaitList waiterList{};
|
||||
};
|
||||
|
||||
}
|
||||
26
mesosphere/include/mesosphere/threading/KCriticalSection.hpp
Normal file
26
mesosphere/include/mesosphere/threading/KCriticalSection.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/interrupts/KInterruptSpinLock.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KCriticalSection final : public KInterruptSpinLock<false> {
|
||||
public:
|
||||
|
||||
bool try_lock();
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
KCriticalSection() = default;
|
||||
KCriticalSection(const KCriticalSection &) = delete;
|
||||
KCriticalSection(KCriticalSection &&) = delete;
|
||||
KCriticalSection &operator=(const KCriticalSection &) = delete;
|
||||
KCriticalSection &operator=(KCriticalSection &&) = delete;
|
||||
|
||||
private:
|
||||
KThread *lockingThread = nullptr;
|
||||
ulong lockCount = 0;
|
||||
};
|
||||
|
||||
}
|
||||
323
mesosphere/include/mesosphere/threading/KMultiLevelQueue.hpp
Normal file
323
mesosphere/include/mesosphere/threading/KMultiLevelQueue.hpp
Normal file
@@ -0,0 +1,323 @@
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<uint depth_, typename IntrusiveListType_, typename PrioGetterType_>
|
||||
class KMultiLevelQueue {
|
||||
static_assert(depth_ <= 64, "Bitfield must be constrained in a u64");
|
||||
public:
|
||||
static constexpr uint depth = depth_;
|
||||
|
||||
using IntrusiveListType = IntrusiveListType_;
|
||||
using PrioGetterType = PrioGetterType_;
|
||||
|
||||
using value_traits = typename IntrusiveListType::value_traits;
|
||||
|
||||
using pointer = typename IntrusiveListType::pointer;
|
||||
using const_pointer = typename IntrusiveListType::const_pointer;
|
||||
using value_type = typename IntrusiveListType::value_type;
|
||||
using reference = typename IntrusiveListType::reference;
|
||||
using const_reference = typename IntrusiveListType::const_reference;
|
||||
using difference_type = typename IntrusiveListType::difference_type;
|
||||
using size_type = typename IntrusiveListType::size_type;
|
||||
|
||||
template<bool isConst>
|
||||
class iterator_impl {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename KMultiLevelQueue::value_type;
|
||||
using difference_type = typename KMultiLevelQueue::difference_type;
|
||||
using pointer = typename std::conditional<
|
||||
isConst,
|
||||
typename KMultiLevelQueue::const_pointer,
|
||||
typename KMultiLevelQueue::pointer>::type;
|
||||
using reference = typename std::conditional<
|
||||
isConst,
|
||||
typename KMultiLevelQueue::const_reference,
|
||||
typename KMultiLevelQueue::reference>::type;
|
||||
|
||||
bool operator==(const iterator_impl &other) const {
|
||||
return (isEnd() && other.isEnd()) || (it == other.it);
|
||||
}
|
||||
|
||||
bool operator!=(const iterator_impl &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference operator*() {
|
||||
return *it;
|
||||
}
|
||||
|
||||
pointer operator->() {
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
iterator_impl &operator++() {
|
||||
if (isEnd()) {
|
||||
return *this;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
if (it == getEndItForPrio()) {
|
||||
u64 prios = mlq.usedPriorities;
|
||||
prios &= ~((1ull << (currentPrio + 1)) - 1);
|
||||
if (prios == 0) {
|
||||
currentPrio = KMultiLevelQueue::depth;
|
||||
} else {
|
||||
currentPrio = __builtin_ffsll(prios) - 1;
|
||||
it = getBeginItForPrio();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_impl &operator--() {
|
||||
if (isEnd()) {
|
||||
if (mlq.usedPriorities != 0) {
|
||||
currentPrio = 63 - __builtin_clzll(mlq.usedPriorities);
|
||||
it = getEndItForPrio();
|
||||
--it;
|
||||
}
|
||||
} else if (it == getBeginItForPrio()) {
|
||||
u64 prios = mlq.usedPriorities;
|
||||
prios &= (1ull << currentPrio) - 1;
|
||||
if (prios != 0) {
|
||||
currentPrio = __builtin_ffsll(prios) - 1;
|
||||
it = getEndItForPrio();
|
||||
--it;
|
||||
}
|
||||
} else {
|
||||
--it;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_impl &operator++(int) {
|
||||
const iterator_impl v{*this};
|
||||
++(*this);
|
||||
return v;
|
||||
}
|
||||
|
||||
iterator_impl &operator--(int) {
|
||||
const iterator_impl v{*this};
|
||||
--(*this);
|
||||
return v;
|
||||
}
|
||||
|
||||
// allow implicit const->non-const
|
||||
iterator_impl(const iterator_impl<false> &other)
|
||||
: mlq(other.mlq), it(other.it), currentPrio(other.currentPrio) {}
|
||||
|
||||
friend class iterator_impl<true>;
|
||||
iterator_impl() = default;
|
||||
private:
|
||||
friend class KMultiLevelQueue;
|
||||
using container_ref = typename std::conditional<
|
||||
isConst,
|
||||
const KMultiLevelQueue &,
|
||||
KMultiLevelQueue &>::type;
|
||||
using list_iterator = typename std::conditional<
|
||||
isConst,
|
||||
typename IntrusiveListType::const_iterator,
|
||||
typename IntrusiveListType::iterator>::type;
|
||||
container_ref mlq;
|
||||
list_iterator it;
|
||||
uint currentPrio;
|
||||
|
||||
explicit iterator_impl(container_ref mlq, list_iterator const &it, uint currentPrio)
|
||||
: mlq(mlq), it(it), currentPrio(currentPrio) {}
|
||||
explicit iterator_impl(container_ref mlq, uint currentPrio)
|
||||
: mlq(mlq), it(), currentPrio(currentPrio) {}
|
||||
constexpr bool isEnd() const {
|
||||
return currentPrio == KMultiLevelQueue::depth;
|
||||
}
|
||||
|
||||
list_iterator getBeginItForPrio() const {
|
||||
if constexpr (isConst) {
|
||||
return mlq.levels[currentPrio].cbegin();
|
||||
} else {
|
||||
return mlq.levels[currentPrio].begin();
|
||||
}
|
||||
}
|
||||
|
||||
list_iterator getEndItForPrio() const {
|
||||
if constexpr (isConst) {
|
||||
return mlq.levels[currentPrio].cend();
|
||||
} else {
|
||||
return mlq.levels[currentPrio].end();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using iterator = iterator_impl<false>;
|
||||
using const_iterator = iterator_impl<true>;
|
||||
|
||||
void add(reference r) {
|
||||
uint prio = prioGetter(r);
|
||||
levels[prio].push_back(r);
|
||||
usedPriorities |= 1ul << prio;
|
||||
}
|
||||
|
||||
void remove(const_reference r) {
|
||||
uint prio = prioGetter(r);
|
||||
levels[prio].erase(levels[prio].iterator_to(r));
|
||||
if (levels[prio].empty()) {
|
||||
usedPriorities &= ~(1ul << prio);
|
||||
}
|
||||
}
|
||||
|
||||
void remove(const_iterator it) {
|
||||
remove(*it);
|
||||
}
|
||||
void erase(const_iterator it) {
|
||||
remove(it);
|
||||
}
|
||||
|
||||
void adjust(const_reference r, uint oldPrio, bool isCurrentThread = false) {
|
||||
uint prio = prioGetter(r);
|
||||
|
||||
// The thread is the current thread if and only if it is first on the running queue of highest priority, so it needs to be first on the dst queue as well.
|
||||
auto newnext = isCurrentThread ? levels[prio].cbegin() : levels[prio].cend();
|
||||
levels[prio].splice(newnext, levels[oldPrio], levels[oldPrio].iterator_to(r));
|
||||
|
||||
usedPriorities |= 1ul << prio;
|
||||
}
|
||||
void adjust(const_iterator it, uint oldPrio, bool isCurrentThread = false) {
|
||||
adjust(*it, oldPrio, isCurrentThread);
|
||||
}
|
||||
|
||||
void transferToFront(const_reference r, KMultiLevelQueue &other) {
|
||||
uint prio = prioGetter(r);
|
||||
other.levels[prio].splice(other.levels[prio].begin(), levels[prio], levels[prio].iterator_to(r));
|
||||
other.usedPriorities |= 1ul << prio;
|
||||
if (levels[prio].empty()) {
|
||||
usedPriorities &= ~(1ul << prio);
|
||||
}
|
||||
}
|
||||
|
||||
void transferToFront(const_iterator it, KMultiLevelQueue &other) {
|
||||
transferToFront(*it, other);
|
||||
}
|
||||
|
||||
void transferToBack(const_reference r, KMultiLevelQueue &other) {
|
||||
uint prio = prioGetter(r);
|
||||
other.levels[prio].splice(other.levels[prio].end(), levels[prio], levels[prio].iterator_to(r));
|
||||
other.usedPriorities |= 1ul << prio;
|
||||
if (levels[prio].empty()) {
|
||||
usedPriorities &= ~(1ul << prio);
|
||||
}
|
||||
}
|
||||
|
||||
void transferToBack(const_iterator it, KMultiLevelQueue &other) {
|
||||
transferToBack(*it, other);
|
||||
}
|
||||
|
||||
void yield(uint prio, size_type n = 1) {
|
||||
levels[prio].shift_forward(n);
|
||||
}
|
||||
void yield(const_reference r) {
|
||||
uint prio = prioGetter(r);
|
||||
if (&r == &levels[prio].front()) {
|
||||
yield(prio, 1);
|
||||
}
|
||||
}
|
||||
|
||||
uint highestPrioritySet(uint maxPrio = 0) {
|
||||
u64 priorities = maxPrio == 0 ? usedPriorities : (usedPriorities & ~((1 << maxPrio) - 1));
|
||||
return priorities == 0 ? depth : (uint)(__builtin_ffsll((long long)priorities) - 1);
|
||||
}
|
||||
|
||||
uint lowestPrioritySet(uint minPrio = depth - 1) {
|
||||
u64 priorities = minPrio >= depth - 1 ? usedPriorities : (usedPriorities & ((1 << (minPrio + 1)) - 1));
|
||||
return priorities == 0 ? depth : 63 - __builtin_clzll(priorities);
|
||||
}
|
||||
|
||||
size_type size(uint prio) const {
|
||||
return levels[prio].size();
|
||||
}
|
||||
bool empty(uint prio) const {
|
||||
return (usedPriorities & (1 << prio)) == 0;
|
||||
}
|
||||
|
||||
size_type size() const {
|
||||
u64 prios = usedPriorities;
|
||||
size_type sz = 0;
|
||||
while (prios != 0) {
|
||||
int ffs = __builtin_ffsll(prios);
|
||||
sz += size((uint)ffs - 1);
|
||||
prios &= ~(1ull << (ffs - 1));
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
bool empty() const {
|
||||
return usedPriorities == 0;
|
||||
}
|
||||
|
||||
reference front(uint maxPrio = 0) {
|
||||
// Undefined behavior if empty
|
||||
uint priority = highestPrioritySet(maxPrio);
|
||||
return levels[priority == depth ? 0 : priority].front();
|
||||
}
|
||||
const_reference front(uint maxPrio = 0) const {
|
||||
// Undefined behavior if empty
|
||||
uint priority = highestPrioritySet(maxPrio);
|
||||
return levels[priority == depth ? 0 : priority].front();
|
||||
}
|
||||
|
||||
reference back(uint minPrio = depth - 1) {
|
||||
// Inclusive
|
||||
// Undefined behavior if empty
|
||||
uint priority = highestPrioritySet(minPrio); // intended
|
||||
return levels[priority == depth ? 63 : priority].back();
|
||||
}
|
||||
const_reference back(uint minPrio = KMultiLevelQueue::depth - 1) const {
|
||||
// Inclusive
|
||||
// Undefined behavior if empty
|
||||
uint priority = highestPrioritySet(minPrio); // intended
|
||||
return levels[priority == depth ? 63 : priority].back();
|
||||
}
|
||||
|
||||
const_iterator cbegin(uint maxPrio = 0) const {
|
||||
uint priority = highestPrioritySet(maxPrio);
|
||||
return priority == depth ? cend() : const_iterator{*this, levels[priority].cbegin(), priority};
|
||||
}
|
||||
iterator begin(uint maxPrio = 0) const {
|
||||
return cbegin(maxPrio);
|
||||
}
|
||||
iterator begin(uint maxPrio = 0) {
|
||||
uint priority = highestPrioritySet(maxPrio);
|
||||
return priority == depth ? end() : iterator{*this, levels[priority].begin(), priority};
|
||||
}
|
||||
|
||||
const_iterator cend(uint minPrio = depth - 1) const {
|
||||
return minPrio == depth - 1 ? const_iterator{*this, depth} : cbegin(minPrio + 1);
|
||||
}
|
||||
const_iterator end(uint minPrio = depth - 1) const {
|
||||
return cend(minPrio);
|
||||
}
|
||||
iterator end(uint minPrio = depth - 1) {
|
||||
return minPrio == depth - 1 ? iterator{*this, depth} : begin(minPrio + 1);
|
||||
}
|
||||
|
||||
void swap(KMultiLevelQueue &other)
|
||||
{
|
||||
std::swap(*this, other);
|
||||
}
|
||||
|
||||
KMultiLevelQueue(PrioGetterType prioGetter) : prioGetter(prioGetter), usedPriorities(0), levels() {};
|
||||
explicit KMultiLevelQueue(const value_traits &traits, PrioGetterType prioGetter = PrioGetterType{})
|
||||
: prioGetter(prioGetter), usedPriorities(0), levels(detail::MakeArrayOf<IntrusiveListType, depth>(traits)) {}
|
||||
|
||||
private:
|
||||
PrioGetterType prioGetter;
|
||||
u64 usedPriorities;
|
||||
std::array<IntrusiveListType, depth> levels;
|
||||
};
|
||||
|
||||
}
|
||||
93
mesosphere/include/mesosphere/threading/KMutex.hpp
Normal file
93
mesosphere/include/mesosphere/threading/KMutex.hpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
/// Fulfills Mutex requirements
|
||||
class KMutex final {
|
||||
public:
|
||||
|
||||
using native_handle_type = uiptr;
|
||||
|
||||
KMutex() = default;
|
||||
KMutex(const KMutex &) = delete;
|
||||
KMutex(KMutex &&) = delete;
|
||||
KMutex &operator=(const KMutex &) = delete;
|
||||
KMutex &operator=(KMutex &&) = delete;
|
||||
|
||||
native_handle_type native_handle() { return tag; }
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
KThread *currentThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
return try_lock_impl_get_owner(currentThread) == nullptr;
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
KThread *currentThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
KThread *owner;
|
||||
|
||||
while ((owner = try_lock_impl_get_owner(currentThread)) != nullptr) {
|
||||
// Our thread may be resumed even if we weren't given the mutex
|
||||
lock_slow_path(*owner, *currentThread);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
// Ensure sequencial ordering, to happen-after mutex load
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
KThread *currentThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
native_handle_type thisThread = (native_handle_type)currentThread;
|
||||
|
||||
/*
|
||||
If we don't have any waiter, just store 0 (free the mutex).
|
||||
Otherwise, or if a race condition happens and a new waiter appears,
|
||||
take the slow path.
|
||||
*/
|
||||
if (tag.load() != thisThread || !tag.compare_exchange_strong(thisThread, 0)) {
|
||||
unlock_slow_path(*currentThread);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
KThread *try_lock_impl_get_owner(KThread *currentThread)
|
||||
{
|
||||
native_handle_type oldTag, newTag;
|
||||
native_handle_type thisThread = (native_handle_type)currentThread;
|
||||
|
||||
oldTag = tag.load();
|
||||
do {
|
||||
// Add "has listener" if the mutex was not free
|
||||
newTag = oldTag == 0 ? thisThread : (oldTag | 1);
|
||||
} while (!tag.compare_exchange_weak(oldTag, newTag, std::memory_order_seq_cst));
|
||||
|
||||
// The mutex was not free or was not ours => return false
|
||||
if(oldTag != 0 && (oldTag & ~1) != thisThread) {
|
||||
return (KThread *)(oldTag &~ 1);
|
||||
} else {
|
||||
/*
|
||||
Ensure sequencial ordering if mutex was acquired
|
||||
and mutex lock happens-before mutex unlock.
|
||||
*/
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void lock_slow_path(KThread &owner, KThread &requester);
|
||||
void unlock_slow_path(KThread &owner);
|
||||
std::atomic<native_handle_type> tag{};
|
||||
};
|
||||
|
||||
}
|
||||
137
mesosphere/include/mesosphere/threading/KScheduler.hpp
Normal file
137
mesosphere/include/mesosphere/threading/KScheduler.hpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/threading/KMultiLevelQueue.hpp>
|
||||
#include <mesosphere/threading/KCriticalSection.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KScheduler {
|
||||
public:
|
||||
class Global {
|
||||
public:
|
||||
using MlqType = KMultiLevelQueue<64, KThread::SchedulerList, __decltype(&KThread::GetPriorityOf)>;
|
||||
Global() = delete;
|
||||
Global(const Global &) = delete;
|
||||
Global(Global &&) = delete;
|
||||
Global &operator=(const Global &) = delete;
|
||||
Global &operator=(Global &&) = delete;
|
||||
|
||||
static MlqType &GetScheduledMlq(uint coreId) { return scheduledMlqs[coreId]; }
|
||||
static MlqType &GetSuggestedMlq(uint coreId) { return suggestedMlqs[coreId]; }
|
||||
|
||||
static void SetThreadRunning(KThread &thread);
|
||||
static void SetThreadPaused(KThread &thread);
|
||||
static void AdjustThreadPriorityChanged(KThread &thread, uint oldPrio, bool isCurrentThread = false);
|
||||
static void AdjustThreadAffinityChanged(KThread &thread, int oldCoreId, u64 oldAffinityMask);
|
||||
static void YieldThread(KThread &thread);
|
||||
static void YieldThreadAndBalanceLoad(KThread &thread);
|
||||
static void YieldThreadAndWaitForLoadBalancing(KThread &thread);
|
||||
|
||||
static void YieldPreemptThread(KThread ¤tKernelHandlerThread, uint coreId, uint maxPrio = 59);
|
||||
|
||||
static void SelectThreads();
|
||||
|
||||
static constexpr uint minRegularPriority = 2;
|
||||
private:
|
||||
friend class KScheduler;
|
||||
|
||||
static void TransferThreadToCore(KThread &thread, int coreId);
|
||||
static void AskForReselectionOrMarkRedundant(KThread *currentThread, KThread *winner);
|
||||
|
||||
// allowSecondPass = true is only used in SelectThreads
|
||||
static KThread *PickOneSuggestedThread(const std::array<KThread *, MAX_CORES> ¤tThreads,
|
||||
uint coreId, bool compareTime = false, bool allowSecondPass = false,
|
||||
uint maxPrio = 0, uint minPrio = MlqType::depth - 1);
|
||||
|
||||
static bool reselectionRequired;
|
||||
static std::array<MlqType, MAX_CORES> scheduledMlqs, suggestedMlqs;
|
||||
|
||||
template<typename F, typename ...Args>
|
||||
static void ApplyReschedulingOperationImpl(F f, KThread &thread, int coreId, u64 affMask, Args&& ...args)
|
||||
{
|
||||
if (coreId >= 0) {
|
||||
f(scheduledMlqs[coreId], thread, std::forward<Args>(args)...);
|
||||
affMask &= ~(1 << coreId);
|
||||
}
|
||||
|
||||
while (affMask != 0) {
|
||||
coreId = __builtin_ffsll(affMask) - 1;
|
||||
f(suggestedMlqs[coreId], thread, std::forward<Args>(args)...);
|
||||
affMask &= ~(1 << coreId);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename ...Args>
|
||||
static void ApplyReschedulingOperation(F f, KThread &thread, Args&& ...args)
|
||||
{
|
||||
u64 aff = thread.GetAffinityMask();
|
||||
int coreId = thread.GetCurrentCoreId();
|
||||
|
||||
ApplyReschedulingOperationImpl(f, thread, coreId, aff, std::forward<Args>(args)...);
|
||||
|
||||
thread.IncrementSchedulerOperationCount();
|
||||
reselectionRequired = true;
|
||||
}
|
||||
};
|
||||
|
||||
KScheduler() = default;
|
||||
KScheduler(const KScheduler &) = delete;
|
||||
KScheduler(KScheduler &&) = delete;
|
||||
KScheduler &operator=(const KScheduler &) = delete;
|
||||
KScheduler &operator=(KScheduler &&) = delete;
|
||||
|
||||
static KCriticalSection &GetCriticalSection() { return criticalSection; }
|
||||
|
||||
static void YieldCurrentThread();
|
||||
static void YieldCurrentThreadAndBalanceLoad();
|
||||
static void YieldCurrentThreadAndWaitForLoadBalancing();
|
||||
|
||||
static void HandleCriticalSectionLeave();
|
||||
friend void SchedulerHandleCriticalSectionLeave()
|
||||
{
|
||||
HandleCriticalSectionLeave();
|
||||
}
|
||||
|
||||
void ForceContextSwitch() {}
|
||||
void ForceContextSwitchAfterIrq() {}
|
||||
|
||||
void SetContextSwitchNeededForWorkQueue() { isContextSwitchNeededForWorkQueue = true; }
|
||||
|
||||
constexpr ulong GetIdleSelectionCount() const { return idleSelectionCount; }
|
||||
constexpr bool IsActive() const { return /*isActive */ true; } // TODO
|
||||
|
||||
private:
|
||||
bool hasContextSwitchStartedAfterIrq;
|
||||
bool isActive;
|
||||
bool isContextSwitchNeeded;
|
||||
bool isContextSwitchNeededForWorkQueue;
|
||||
uint coreId;
|
||||
u64 lastContextSwitchTime;
|
||||
KThread *selectedThread;
|
||||
KThread *previousThread;
|
||||
ulong idleSelectionCount;
|
||||
|
||||
void *tmpStack;
|
||||
KThread *idleThread;
|
||||
|
||||
static KCriticalSection criticalSection;
|
||||
|
||||
template<typename F>
|
||||
void DoYieldOperation(F f, KThread ¤tThread)
|
||||
{
|
||||
if (!currentThread.IsSchedulerOperationRedundant())
|
||||
{
|
||||
criticalSection.lock();
|
||||
f(currentThread);
|
||||
criticalSection.unlock();
|
||||
ForceContextSwitch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/threading/KScheduler.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KScopedCriticalSection final {
|
||||
private:
|
||||
std::scoped_lock<KCriticalSection> lk{KScheduler::GetCriticalSection()};
|
||||
};
|
||||
|
||||
}
|
||||
346
mesosphere/include/mesosphere/threading/KThread.hpp
Normal file
346
mesosphere/include/mesosphere/threading/KThread.hpp
Normal file
@@ -0,0 +1,346 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Handle.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/interfaces/IAlarmable.hpp>
|
||||
#include <mesosphere/interfaces/ILimitedResource.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct LightSessionRequest;
|
||||
|
||||
struct KThreadContext;
|
||||
|
||||
struct ThreadProcessListTag;
|
||||
struct ThreadWaitListTag;
|
||||
struct ThreadMutexWaitListTag;
|
||||
using ThreadProcessListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::tag<ThreadProcessListTag> >;
|
||||
using ThreadWaitListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::tag<ThreadWaitListTag> >;
|
||||
using ThreadMutexWaitListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::tag<ThreadMutexWaitListTag> >;
|
||||
|
||||
class KThread final :
|
||||
public KAutoObject,
|
||||
public ILimitedResource<KThread>,
|
||||
public ISetAllocated<KThread>,
|
||||
public IAlarmable,
|
||||
public ThreadProcessListBaseHook,
|
||||
public ThreadWaitListBaseHook,
|
||||
public ThreadMutexWaitListBaseHook
|
||||
{
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, Thread);
|
||||
MESOSPHERE_LIMITED_RESOURCE_TRAITS(100ms);
|
||||
|
||||
class StackParameters final {
|
||||
public:
|
||||
StackParameters(std::array<u64, 4> svcPermissionMask, KThreadContext *threadCtx) :
|
||||
svcPermissionMask{svcPermissionMask}, threadCtx{threadCtx} {}
|
||||
|
||||
void Initialize(std::array<u64, 4> svcPermissionMask, KThreadContext *threadCtx)
|
||||
{
|
||||
*this = StackParameters{svcPermissionMask, threadCtx};
|
||||
}
|
||||
|
||||
constexpr bool IsExecutingSvc() const { return isExecutingSvc; }
|
||||
constexpr u8 GetCurrentSvcId() const { return currentSvcId; }
|
||||
|
||||
constexpr uint GetInterruptBottomHalfLockCount() const { return interruptBottomHalfLockCount; }
|
||||
void IncrementInterruptBottomHalfLockCount() { ++interruptBottomHalfLockCount; }
|
||||
void DecrementInterruptBottomHalfLockCount() { --interruptBottomHalfLockCount; }
|
||||
|
||||
KThreadContext *GetThreadContext() const { return threadCtx; }
|
||||
|
||||
private:
|
||||
std::array<u64, 4> svcPermissionMask[256/64]{};
|
||||
u8 stateFlags = 0;
|
||||
u8 currentSvcId = 0;
|
||||
bool isExecutingSvc = false;
|
||||
bool isNotStarted = true;
|
||||
uint interruptBottomHalfLockCount = 1;
|
||||
KThreadContext *threadCtx = nullptr;
|
||||
};
|
||||
|
||||
struct SchedulerValueTraits {
|
||||
using node_traits = boost::intrusive::list_node_traits<KThread *>;
|
||||
using node = node_traits::node;
|
||||
using node_ptr = node *;
|
||||
using const_node_ptr = const node *;
|
||||
using value_type = KThread;
|
||||
using pointer = KThread *;
|
||||
using const_pointer = const KThread *;
|
||||
static constexpr boost::intrusive::link_mode_type link_mode = boost::intrusive::normal_link;
|
||||
|
||||
constexpr SchedulerValueTraits(uint coreId) : coreId(coreId) {}
|
||||
node_ptr to_node_ptr (value_type &value) const {
|
||||
return &value.schedulerNodes[coreId];
|
||||
}
|
||||
const_node_ptr to_node_ptr (const value_type &value) const {
|
||||
return &value.schedulerNodes[coreId];
|
||||
}
|
||||
pointer to_value_ptr(node_ptr n) const {
|
||||
return detail::GetParentFromArrayMember(n, coreId, &KThread::schedulerNodes);
|
||||
}
|
||||
const_pointer to_value_ptr(const_node_ptr n) const {
|
||||
return detail::GetParentFromArrayMember(n, coreId, &KThread::schedulerNodes);
|
||||
}
|
||||
|
||||
private:
|
||||
uint coreId;
|
||||
};
|
||||
|
||||
enum class SchedulingStatus : u16 {
|
||||
Paused = 1,
|
||||
Running = 2,
|
||||
Exited = 3,
|
||||
};
|
||||
|
||||
enum class ForcePauseReason : u16 {
|
||||
ThreadActivity = 0,
|
||||
ProcessActivity = 1,
|
||||
Debug = 2,
|
||||
Reserved = 3,
|
||||
KernelLoading = 4,
|
||||
};
|
||||
|
||||
|
||||
using ProcessList = typename boost::intrusive::make_list<
|
||||
KThread,
|
||||
boost::intrusive::base_hook<ThreadProcessListBaseHook>
|
||||
>::type;
|
||||
|
||||
using SchedulerList = typename boost::intrusive::make_list<
|
||||
KThread,
|
||||
boost::intrusive::value_traits<KThread::SchedulerValueTraits>
|
||||
>::type;
|
||||
|
||||
using WaitList = typename boost::intrusive::make_list<
|
||||
KThread,
|
||||
boost::intrusive::base_hook<ThreadWaitListBaseHook>,
|
||||
boost::intrusive::constant_time_size<false>
|
||||
>::type;
|
||||
|
||||
private:
|
||||
using MutexWaitList = typename boost::intrusive::make_list<
|
||||
KThread,
|
||||
boost::intrusive::base_hook<ThreadMutexWaitListBaseHook>
|
||||
>::type;
|
||||
|
||||
public:
|
||||
|
||||
virtual void OnAlarm() override;
|
||||
|
||||
static constexpr uint GetPriorityOf(const KThread &thread)
|
||||
{
|
||||
return thread.priority;
|
||||
}
|
||||
|
||||
constexpr uint GetPriority() const { return priority; }
|
||||
constexpr u64 GetId() const { return id; }
|
||||
constexpr int GetCurrentCoreId() const { return currentCoreId; }
|
||||
constexpr ulong GetAffinityMask() const { return affinityMask; }
|
||||
constexpr long GetLastScheduledTime() const { return lastScheduledTime; }
|
||||
|
||||
StackParameters &GetStackParameters()
|
||||
{
|
||||
return *(StackParameters *)(kernelStackTop - sizeof(StackParameters));
|
||||
}
|
||||
|
||||
KProcess *GetOwner() const { return owner; }
|
||||
bool IsSchedulerOperationRedundant() const;
|
||||
|
||||
void IncrementSchedulerOperationCount();
|
||||
void SetRedundantSchedulerOperation();
|
||||
void SetCurrentCoreId(int coreId) { currentCoreId = coreId; }
|
||||
|
||||
void SetProcessLastThreadAndIdleSelectionCount(ulong idleSelectionCount);
|
||||
|
||||
void UpdateLastScheduledTime() { ++lastScheduledTime; /* FIXME */}
|
||||
|
||||
constexpr SchedulingStatus GetSchedulingStatus() const
|
||||
{
|
||||
return (SchedulingStatus)(currentSchedMaskFull & 0xF);
|
||||
}
|
||||
constexpr bool IsForcePausedFor(ForcePauseReason reason) const
|
||||
{
|
||||
return (schedMaskForForcePauseFull & (1 << (4 + ((ushort)reason)))) != 0;
|
||||
}
|
||||
constexpr bool IsForcePaused() const
|
||||
{
|
||||
return (schedMaskForForcePauseFull & ~0xF) != 0;
|
||||
}
|
||||
static constexpr bool CompareSchedulingStatusFull(ushort fullMask, SchedulingStatus status)
|
||||
{
|
||||
return fullMask == (ushort)status;
|
||||
}
|
||||
constexpr bool CompareSchedulingStatusFull(SchedulingStatus status) const
|
||||
{
|
||||
return CompareSchedulingStatusFull(schedMaskForForcePauseFull, status);
|
||||
}
|
||||
|
||||
/// Returns old full mask
|
||||
ushort SetSchedulingStatusField(SchedulingStatus status)
|
||||
{
|
||||
ushort oldMaskFull = currentSchedMaskFull;
|
||||
currentSchedMaskFull = (currentSchedMaskFull & ~0xF) | ((ushort)status & 0xF);
|
||||
return oldMaskFull;
|
||||
}
|
||||
void AddForcePauseReasonToField(ForcePauseReason reason)
|
||||
{
|
||||
schedMaskForForcePauseFull |= 1 << (4 + ((ushort)reason));
|
||||
}
|
||||
void RemoveForcePauseReasonToField(ForcePauseReason reason)
|
||||
{
|
||||
schedMaskForForcePauseFull |= ~(1 << (4 + ((ushort)reason)));
|
||||
}
|
||||
|
||||
ushort CommitForcePauseToField()
|
||||
{
|
||||
|
||||
ushort oldMaskFull = currentSchedMaskFull;
|
||||
currentSchedMaskFull = (schedMaskForForcePauseFull & ~0xF) | (currentSchedMaskFull & 0xF);
|
||||
return oldMaskFull;
|
||||
}
|
||||
ushort RevertForcePauseToField()
|
||||
{
|
||||
ushort oldMaskFull = currentSchedMaskFull;
|
||||
currentSchedMaskFull &= 0xF;
|
||||
return oldMaskFull;
|
||||
}
|
||||
|
||||
void AdjustScheduling(ushort oldMaskFull);
|
||||
void Reschedule(SchedulingStatus newStatus);
|
||||
/// Sets status regardless of force-pausing.
|
||||
void RescheduleIfStatusEquals(SchedulingStatus expectedStatus, SchedulingStatus newStatus);
|
||||
void AddForcePauseReason(ForcePauseReason reason);
|
||||
void RemoveForcePauseReason(ForcePauseReason reason);
|
||||
|
||||
bool IsDying() const
|
||||
{
|
||||
// Or already dead
|
||||
/*
|
||||
terminationWanted is only set on exit, under scheduler critical section, to true,
|
||||
and the readers are either a thread under critical section (most common), or end-of-irq/svc/other exception,
|
||||
therefore synchronization outside critsec can be implemented through fences, I think
|
||||
*/
|
||||
return CompareSchedulingStatusFull(SchedulingStatus::Exited) || terminationWanted;
|
||||
}
|
||||
|
||||
void SetTerminationWanted()
|
||||
{
|
||||
terminationWanted = true;
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
/// Takes effect when critical section is left
|
||||
bool WaitForKernelSync(WaitList &waitList);
|
||||
/// Takes effect when critical section is left
|
||||
void ResumeFromKernelSync();
|
||||
/// Takes effect when critical section is left
|
||||
void ResumeFromKernelSync(Result res);
|
||||
/// Takes effect when critical section is left -- all threads in waitlist
|
||||
static void ResumeAllFromKernelSync(WaitList &waitList);
|
||||
/// Takes effect when critical section is left -- all threads in waitlist
|
||||
static void ResumeAllFromKernelSync(WaitList &waitList, Result res);
|
||||
/// Takes effect immediately
|
||||
void CancelKernelSync();
|
||||
/// Takes effect immediately
|
||||
void CancelKernelSync(Result res);
|
||||
/// Needs to be in kernel sync
|
||||
bool IsInKernelSync() const { return currentWaitList != nullptr; }
|
||||
|
||||
/// User sync
|
||||
constexpr bool IsWaitingSync() const { return isWaitingSync; }
|
||||
void SetWaitingSync(bool isWaitingSync) { this->isWaitingSync = isWaitingSync; }
|
||||
constexpr bool IsSyncCancelled() const { return isSyncCancelled; }
|
||||
void SetSyncCancelled(bool isSyncCancelled) { this->isSyncCancelled = isSyncCancelled; }
|
||||
void ClearSync()
|
||||
{
|
||||
signaledSyncObject = nullptr;
|
||||
syncResult = ResultSuccess();
|
||||
}
|
||||
|
||||
constexpr Result GetSyncResult() const { return syncResult; }
|
||||
|
||||
/// Takes effect when critical section is left
|
||||
void HandleSyncObjectSignaled(KSynchronizationObject *syncObj);
|
||||
|
||||
LightSessionRequest *GetCurrentLightSessionRequest() const { return currentLightSessionRequest; }
|
||||
void SetCurrentLightSessionRequest(LightSessionRequest *currentLightSessionRequest)
|
||||
{
|
||||
this->currentLightSessionRequest = currentLightSessionRequest;
|
||||
}
|
||||
|
||||
template<typename Clock, typename Duration>
|
||||
Result WaitSynchronization(int &outId, KSynchronizationObject **syncObjs, int numSyncObjs, const std::chrono::time_point<Clock, Duration> &timeoutTime)
|
||||
{
|
||||
return WaitSynchronizationImpl(outId, syncObjs, numSyncObjs, timeoutTime);
|
||||
}
|
||||
|
||||
constexpr size_t GetNumberOfKMutexWaiters() const { return numKernelMutexWaiters; }
|
||||
constexpr uiptr GetWantedMutex() const { return wantedMutex; }
|
||||
void SetWantedMutex(uiptr mtx) { wantedMutex = mtx; }
|
||||
|
||||
void AddMutexWaiter(KThread &waiter);
|
||||
KThread *RelinquishMutex(size_t *count, uiptr mutexAddr);
|
||||
void RemoveMutexWaiter(KThread &waiter);
|
||||
void InheritDynamicPriority();
|
||||
|
||||
KThread() = default;
|
||||
KThread(KProcess *owner, u64 id, uint priority) : KAutoObject(), owner(owner), schedulerNodes(),
|
||||
id(id), basePriority(priority), priority(priority),
|
||||
currentCoreId(0), affinityMask(15) {};
|
||||
|
||||
friend void IncrementThreadInterruptBottomHalfLockCount(KThread &thread)
|
||||
{
|
||||
thread.GetStackParameters().IncrementInterruptBottomHalfLockCount();
|
||||
}
|
||||
friend void DecrementThreadInterruptBottomHalfLockCount(KThread &thread)
|
||||
{
|
||||
thread.GetStackParameters().DecrementInterruptBottomHalfLockCount();
|
||||
}
|
||||
private:
|
||||
Result WaitSynchronizationImpl(int &outId, KSynchronizationObject **syncObjs, int numSyncObjs, const KSystemClock::time_point &timeoutTime);
|
||||
|
||||
void AddToMutexWaitList(KThread &thread);
|
||||
MutexWaitList::iterator RemoveFromMutexWaitList(MutexWaitList::const_iterator it);
|
||||
void RemoveFromMutexWaitList(const KThread &t);
|
||||
|
||||
KProcess *owner = nullptr;
|
||||
|
||||
boost::intrusive::list_node_traits<KThread *>::node schedulerNodes[4]{};
|
||||
|
||||
WaitList *currentWaitList = nullptr;
|
||||
|
||||
u64 id = 0;
|
||||
long redundantSchedulerOperationCount = 0;
|
||||
ushort currentSchedMaskFull = (ushort)SchedulingStatus::Paused;
|
||||
ushort schedMaskForForcePauseFull = 0;
|
||||
bool terminationWanted = false;
|
||||
uint basePriority = 64, priority = 64;
|
||||
int currentCoreId = -1;
|
||||
ulong affinityMask = 0;
|
||||
bool isSyncCancelled = false;
|
||||
bool isWaitingSync = false;
|
||||
LightSessionRequest *currentLightSessionRequest = nullptr; // located in kernel thread stacks
|
||||
uiptr wantedMutex = 0;
|
||||
KThread *wantedMutexOwner = nullptr;
|
||||
MutexWaitList mutexWaitList{};
|
||||
size_t numKernelMutexWaiters = 0;
|
||||
uiptr kernelStackTop = 0;
|
||||
|
||||
KSynchronizationObject *signaledSyncObject = nullptr;
|
||||
Result syncResult = ResultSuccess();
|
||||
|
||||
u64 lastScheduledTime = 0;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(Thread);
|
||||
|
||||
}
|
||||
10
mesosphere/source/core/KAutoObject.cpp
Normal file
10
mesosphere/source/core/KAutoObject.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KAutoObject::~KAutoObject()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
11
mesosphere/source/core/KCoreContext.cpp
Normal file
11
mesosphere/source/core/KCoreContext.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/threading/KScheduler.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
static KScheduler scheds[4];
|
||||
|
||||
std::array<KCoreContext, MAX_CORES> KCoreContext::instances{ &scheds[0], &scheds[1], &scheds[2], &scheds[3] };
|
||||
|
||||
}
|
||||
79
mesosphere/source/core/KObjectRegistry.cpp
Normal file
79
mesosphere/source/core/KObjectRegistry.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <mesosphere/core/KObjectRegistry.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KObjectRegistry KObjectRegistry::instance{};
|
||||
|
||||
const KObjectRegistry::Node *KObjectRegistry::FindImpl(const char *name) const
|
||||
{
|
||||
auto it = std::find_if(
|
||||
nameNodes.cbegin(),
|
||||
nameNodes.cend(),
|
||||
[name](const Node &nd) {
|
||||
return std::strncmp(name, nd.name, sizeof(nd.name)) == 0;
|
||||
}
|
||||
);
|
||||
|
||||
return it == nameNodes.cend() ? nullptr : &*it;
|
||||
}
|
||||
|
||||
KObjectRegistry::Node *KObjectRegistry::FindImpl(const char *name)
|
||||
{
|
||||
auto it = std::find_if(
|
||||
nameNodes.begin(),
|
||||
nameNodes.end(),
|
||||
[name](const Node &nd) {
|
||||
return std::strncmp(name, nd.name, sizeof(nd.name)) == 0;
|
||||
}
|
||||
);
|
||||
|
||||
return it == nameNodes.end() ? nullptr : &*it;
|
||||
}
|
||||
|
||||
SharedPtr<KAutoObject> KObjectRegistry::Find(const char *name) const
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
const Node *node = FindImpl(name);
|
||||
return node == nullptr ? nullptr : node->obj;
|
||||
}
|
||||
|
||||
|
||||
Result KObjectRegistry::Register(SharedPtr<KAutoObject> obj, const char *name)
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
Node *node = FindImpl(name);
|
||||
if (node != nullptr) {
|
||||
// Name already exists, just replace the auto object.
|
||||
node->obj = std::move(obj);
|
||||
return ResultSuccess();
|
||||
} else {
|
||||
if (nameNodes.emplace_back_or_fail(std::move(obj), name) == nullptr) {
|
||||
return ResultKernelInvalidState();
|
||||
}
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
Result KObjectRegistry::Unregister(const char *name)
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
|
||||
auto it = std::find_if(
|
||||
nameNodes.cbegin(),
|
||||
nameNodes.cend(),
|
||||
[name](const Node &nd) {
|
||||
return std::strncmp(name, nd.name, sizeof(nd.name)) == 0;
|
||||
}
|
||||
);
|
||||
|
||||
if (it == nameNodes.cend()) {
|
||||
return ResultKernelNotFound();
|
||||
} else {
|
||||
nameNodes.erase(it);
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
36
mesosphere/source/core/KSynchronizationObject.cpp
Normal file
36
mesosphere/source/core/KSynchronizationObject.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KSynchronizationObject::~KSynchronizationObject()
|
||||
{
|
||||
}
|
||||
|
||||
void KSynchronizationObject::NotifyWaiters()
|
||||
{
|
||||
KScopedCriticalSection criticalSection;
|
||||
|
||||
if (IsSignaled()) {
|
||||
for (auto &&waiter : waiters) {
|
||||
waiter->HandleSyncObjectSignaled(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KLinkedList<KThread *>::const_iterator KSynchronizationObject::AddWaiter(KThread &thread)
|
||||
{
|
||||
return waiters.insert(waiters.end(), &thread);
|
||||
}
|
||||
|
||||
void KSynchronizationObject::RemoveWaiter(KLinkedList<KThread *>::const_iterator it)
|
||||
{
|
||||
waiters.erase(it);
|
||||
}
|
||||
|
||||
}
|
||||
20
mesosphere/source/interfaces/IAlarmable.cpp
Normal file
20
mesosphere/source/interfaces/IAlarmable.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <mesosphere/interfaces/IAlarmable.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/interrupts/KAlarm.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
void IAlarmable::SetAlarmTimeImpl(const KSystemClock::time_point &alarmTime)
|
||||
{
|
||||
this->alarmTime = alarmTime;
|
||||
KCoreContext::GetCurrentInstance().GetAlarm()->AddAlarmable(*this);
|
||||
}
|
||||
|
||||
void IAlarmable::ClearAlarm()
|
||||
{
|
||||
KCoreContext::GetCurrentInstance().GetAlarm()->RemoveAlarmable(*this);
|
||||
alarmTime = KSystemClock::time_point{};
|
||||
}
|
||||
|
||||
}
|
||||
12
mesosphere/source/interfaces/IInterruptibleWork.cpp
Normal file
12
mesosphere/source/interfaces/IInterruptibleWork.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <mesosphere/interfaces/IInterruptibleWork.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
IWork *IInterruptibleWork::HandleInterrupt(uint interruptId)
|
||||
{
|
||||
(void)interruptId;
|
||||
return (IWork *)this;
|
||||
}
|
||||
|
||||
}
|
||||
26
mesosphere/source/interfaces/ILimitedResource.cpp
Normal file
26
mesosphere/source/interfaces/ILimitedResource.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <mesosphere/interfaces/ILimitedResource.hpp>
|
||||
#include <mesosphere/processes/KProcess.hpp>
|
||||
#include <mesosphere/kresources/KResourceLimit.hpp>
|
||||
|
||||
namespace mesosphere::detail
|
||||
{
|
||||
|
||||
void ReleaseResource(const SharedPtr<KResourceLimit> &reslimit, KAutoObject::TypeId typeId, size_t count, size_t realCount)
|
||||
{
|
||||
if (reslimit != nullptr) {
|
||||
reslimit->Release(KResourceLimit::GetCategory(typeId), count, realCount);
|
||||
} else {
|
||||
KResourceLimit::GetDefaultInstance().Release(KResourceLimit::GetCategory(typeId), count, realCount);
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseResource(const SharedPtr<KProcess> &owner, KAutoObject::TypeId typeId, size_t count, size_t realCount)
|
||||
{
|
||||
if (owner != nullptr) {
|
||||
return ReleaseResource(owner->GetResourceLimit(), typeId, count, realCount);
|
||||
} else {
|
||||
KResourceLimit::GetDefaultInstance().Release(KResourceLimit::GetCategory(typeId), count, realCount);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
56
mesosphere/source/interrupts/KAlarm.cpp
Normal file
56
mesosphere/source/interrupts/KAlarm.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <mesosphere/interrupts/KAlarm.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
#include <mesosphere/arch/KInterruptMaskGuard.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
void KAlarm::AddAlarmable(IAlarmable &alarmable)
|
||||
{
|
||||
std::scoped_lock guard{spinlock};
|
||||
alarmables.insert(alarmable);
|
||||
|
||||
KSystemClock::SetAlarm(alarmables.cbegin()->GetAlarmTime());
|
||||
}
|
||||
|
||||
void KAlarm::RemoveAlarmable(const IAlarmable &alarmable)
|
||||
{
|
||||
std::scoped_lock guard{spinlock};
|
||||
alarmables.erase(alarmable);
|
||||
|
||||
KSystemClock::SetAlarm(alarmables.cbegin()->GetAlarmTime());
|
||||
}
|
||||
|
||||
void KAlarm::HandleAlarm()
|
||||
{
|
||||
{
|
||||
KScopedCriticalSection critsec{};
|
||||
std::scoped_lock guard{spinlock};
|
||||
|
||||
KSystemClock::SetInterruptMasked(true); // mask timer interrupt
|
||||
KSystemClock::time_point currentTime = KSystemClock::now(), maxAlarmTime;
|
||||
while (alarmables.begin() != alarmables.end()) {
|
||||
IAlarmable &a = *alarmables.begin();
|
||||
maxAlarmTime = a.alarmTime;
|
||||
if (maxAlarmTime > currentTime) {
|
||||
break;
|
||||
}
|
||||
|
||||
alarmables.erase(a);
|
||||
a.alarmTime = KSystemClock::time_point{};
|
||||
|
||||
a.OnAlarm();
|
||||
}
|
||||
|
||||
if (maxAlarmTime > KSystemClock::time_point{}) {
|
||||
KSystemClock::SetAlarm(maxAlarmTime);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// TODO Reenable interrupt 30
|
||||
KInterruptMaskGuard guard{};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
19
mesosphere/source/interrupts/KInterruptEvent.cpp
Normal file
19
mesosphere/source/interrupts/KInterruptEvent.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <mesosphere/interrupts/KInterruptEvent.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KInterruptEvent::~KInterruptEvent()
|
||||
{
|
||||
// delete receiver?
|
||||
}
|
||||
|
||||
Result KInterruptEvent::Initialize(int irqId, u32 flags)
|
||||
{
|
||||
// TODO implement
|
||||
(void)flags;
|
||||
this->irqId = irqId;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
45
mesosphere/source/interrupts/KWorkQueue.cpp
Normal file
45
mesosphere/source/interrupts/KWorkQueue.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <mesosphere/interrupts/KWorkQueue.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/threading/KScheduler.hpp>
|
||||
#include <mesosphere/arch/KInterruptMaskGuard.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
void KWorkQueue::AddWork(IWork &work)
|
||||
{
|
||||
workQueue.push_back(work);
|
||||
KCoreContext::GetCurrentInstance().GetScheduler()->SetContextSwitchNeededForWorkQueue();
|
||||
}
|
||||
|
||||
void KWorkQueue::Initialize()
|
||||
{
|
||||
//handlerThread.reset(new KThread); //TODO!
|
||||
kassert(handlerThread == nullptr);
|
||||
}
|
||||
|
||||
void KWorkQueue::HandleWorkQueue()
|
||||
{
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
while (true) {
|
||||
IWork *work = nullptr;
|
||||
do {
|
||||
KInterruptMaskGuard imguard{};
|
||||
auto it = workQueue.begin();
|
||||
if (it != workQueue.end()) {
|
||||
work = &*it;
|
||||
workQueue.erase(it);
|
||||
} else {
|
||||
{
|
||||
//TODO: thread usercontext scheduler/bottom hard guard
|
||||
cctx.GetCurrentThread()->Reschedule(KThread::SchedulingStatus::Paused);
|
||||
}
|
||||
cctx.GetScheduler()->ForceContextSwitch();
|
||||
}
|
||||
} while (work == nullptr);
|
||||
|
||||
work->DoWork();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
81
mesosphere/source/kresources/KResourceLimit.cpp
Normal file
81
mesosphere/source/kresources/KResourceLimit.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <mesosphere/kresources/KResourceLimit.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KResourceLimit KResourceLimit::defaultInstance{};
|
||||
|
||||
size_t KResourceLimit::GetCurrentValue(KResourceLimit::Category category) const
|
||||
{
|
||||
// Caller should check category
|
||||
std::scoped_lock guard{condvar.mutex()};
|
||||
return currentValues[(uint)category];
|
||||
}
|
||||
|
||||
size_t KResourceLimit::GetLimitValue(KResourceLimit::Category category) const
|
||||
{
|
||||
// Caller should check category
|
||||
std::scoped_lock guard{condvar.mutex()};
|
||||
return limitValues[(uint)category];
|
||||
}
|
||||
|
||||
size_t KResourceLimit::GetRemainingValue(KResourceLimit::Category category) const
|
||||
{
|
||||
// Caller should check category
|
||||
std::scoped_lock guard{condvar.mutex()};
|
||||
return limitValues[(uint)category] - currentValues[(uint)category];
|
||||
}
|
||||
|
||||
bool KResourceLimit::SetLimitValue(KResourceLimit::Category category, size_t value)
|
||||
{
|
||||
std::scoped_lock guard{condvar.mutex()};
|
||||
if ((long)value < 0 || currentValues[(uint)category] > value) {
|
||||
return false;
|
||||
} else {
|
||||
limitValues[(uint)category] = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void KResourceLimit::Release(KResourceLimit::Category category, size_t count, size_t realCount)
|
||||
{
|
||||
// Caller should ensure parameters are correct
|
||||
std::scoped_lock guard{condvar.mutex()};
|
||||
currentValues[(uint)category] -= count;
|
||||
realValues[(uint)category] -= realCount;
|
||||
condvar.notify_all();
|
||||
}
|
||||
|
||||
bool KResourceLimit::ReserveDetail(KResourceLimit::Category category, size_t count, const KSystemClock::time_point &timeoutTime)
|
||||
{
|
||||
std::scoped_lock guard{condvar.mutex()};
|
||||
if ((long)count <= 0 || realValues[(uint)category] >= limitValues[(uint)category]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t newCur = currentValues[(uint)category] + count;
|
||||
bool ok = false;
|
||||
|
||||
auto condition =
|
||||
[=, &newCur] {
|
||||
newCur = this->currentValues[(uint)category] + count;
|
||||
size_t lval = this->limitValues[(uint)category];
|
||||
return this->realValues[(uint)category] <= lval && newCur <= lval; // need to check here
|
||||
};
|
||||
|
||||
if (timeoutTime <= KSystemClock::never) {
|
||||
ok = true;
|
||||
condvar.wait(condition);
|
||||
} else {
|
||||
ok = condvar.wait_until(timeoutTime, condition);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
currentValues[(uint)category] += count;
|
||||
realValues[(uint)category] += count;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
}
|
||||
1141
mesosphere/source/my_libc.c
Normal file
1141
mesosphere/source/my_libc.c
Normal file
File diff suppressed because it is too large
Load Diff
23
mesosphere/source/my_libstdc++.cpp
Normal file
23
mesosphere/source/my_libstdc++.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <cstddef>
|
||||
|
||||
void *operator new(std::size_t) { for(;;); }
|
||||
void *operator new[](std::size_t) { for(;;); }
|
||||
void operator delete(void *) { }
|
||||
void operator delete(void *, std::size_t) { }
|
||||
void operator delete[](void *) { }
|
||||
void operator delete[](void *, std::size_t) { }
|
||||
|
||||
extern "C" {
|
||||
|
||||
int
|
||||
__cxa_atexit (void (*fn) (void *),
|
||||
void *arg,
|
||||
void *d)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void *__dso_handle = 0;
|
||||
|
||||
[[noreturn]] void __cxa_pure_virtual() { for (;;); }
|
||||
|
||||
}
|
||||
44
mesosphere/source/processes/KClientPort.cpp
Normal file
44
mesosphere/source/processes/KClientPort.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <mesosphere/processes/KPort.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/core/make_object.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KClientPort::~KClientPort()
|
||||
{
|
||||
KScopedCriticalSection critsec{};
|
||||
parent->isClientAlive = false;
|
||||
}
|
||||
|
||||
bool KClientPort::IsSignaled() const
|
||||
{
|
||||
return numSessions.load() < maxSessions;
|
||||
}
|
||||
|
||||
std::tuple<Result, SharedPtr<KLightClientSession>> KClientPort::ConnectLight()
|
||||
{
|
||||
using RetType = std::tuple<Result, SharedPtr<KLightClientSession>>;
|
||||
// Official kernel first checks reslimit then session max count. We will do the opposite.
|
||||
int curCount = numSessions.load();
|
||||
while (curCount < maxSessions || !numSessions.compare_exchange_weak(curCount, curCount + 1));
|
||||
|
||||
if (curCount >= maxSessions) {
|
||||
return RetType{ResultKernelOutOfSessions(), nullptr};
|
||||
}
|
||||
|
||||
auto [res, serverSession, clientSession] = MakeObject<KLightSession>(parent.get());
|
||||
if (res.IsSuccess()) {
|
||||
serverSession.detach(); // Lifetime is now managed my KServerPort session list
|
||||
return RetType{ResultSuccess(), clientSession};
|
||||
} else {
|
||||
if (numSessions.fetch_sub(1) == maxSessions) {
|
||||
NotifyWaiters();
|
||||
}
|
||||
return RetType{res, nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
mesosphere/source/processes/KEvent.cpp
Normal file
20
mesosphere/source/processes/KEvent.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <mesosphere/processes/KEvent.hpp>
|
||||
#include <mesosphere/processes/KProcess.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KEvent::~KEvent()
|
||||
{
|
||||
}
|
||||
|
||||
Result KEvent::Initialize()
|
||||
{
|
||||
SetClientServerParent();
|
||||
server.client = &client;
|
||||
SetResourceOwner(KCoreContext::GetCurrentInstance().GetCurrentProcess());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
130
mesosphere/source/processes/KHandleTable.cpp
Normal file
130
mesosphere/source/processes/KHandleTable.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include <mesosphere/processes/KHandleTable.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/processes/KProcess.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
bool KHandleTable::IsValid(Handle handle) const
|
||||
{
|
||||
// Official kernel checks for nullptr, however this makes the deferred-init logic more difficult.
|
||||
// We use our own, more secure, logic instead, that is, free entries are <= 0.
|
||||
return handle.index < capacity && handle.id > 0 && entries[handle.index].id == handle.id;
|
||||
}
|
||||
|
||||
SharedPtr<KAutoObject> KHandleTable::GetAutoObject(Handle handle) const
|
||||
{
|
||||
if (!handle.IsAliasOrFree()) {
|
||||
// Note: official kernel locks the spinlock here, but we don't need to.
|
||||
return nullptr;
|
||||
} else {
|
||||
std::scoped_lock guard{spinlock};
|
||||
return IsValid(handle) ? entries[handle.index].object : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<KThread> KHandleTable::GetThread(Handle handle, bool allowAlias) const
|
||||
{
|
||||
if (allowAlias && handle == selfThreadAlias) {
|
||||
return KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
} else {
|
||||
return DynamicObjectCast<KThread>(GetAutoObject(handle));
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<KProcess> KHandleTable::GetProcess(Handle handle, bool allowAlias) const
|
||||
{
|
||||
if (allowAlias && handle == selfProcessAlias) {
|
||||
return KCoreContext::GetCurrentInstance().GetCurrentProcess();
|
||||
} else {
|
||||
return DynamicObjectCast<KProcess>(GetAutoObject(handle));
|
||||
}
|
||||
}
|
||||
|
||||
bool KHandleTable::Close(Handle handle)
|
||||
{
|
||||
SharedPtr<KAutoObject> tmp{nullptr}; // ensure any potential dtor is called w/o the spinlock being held
|
||||
|
||||
if (handle.IsAliasOrFree()) {
|
||||
return false;
|
||||
} else {
|
||||
std::scoped_lock guard{spinlock};
|
||||
if (IsValid(handle)) {
|
||||
entries[-firstFreeIndex].id = firstFreeIndex;
|
||||
firstFreeIndex = -(s16)handle.index;
|
||||
--numActive;
|
||||
tmp = std::move(entries[handle.index].object);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<Result, Handle> KHandleTable::Generate(SharedPtr<KAutoObject> obj)
|
||||
{
|
||||
// Note: nullptr is accepted, for deferred-init.
|
||||
|
||||
std::scoped_lock guard{spinlock};
|
||||
if (numActive >= capacity) {
|
||||
return {ResultKernelOutOfHandles(), Handle{}};
|
||||
}
|
||||
|
||||
// Get/allocate the entry
|
||||
u16 index = (u16)-firstFreeIndex;
|
||||
Entry *e = &entries[-firstFreeIndex];
|
||||
firstFreeIndex = e->id;
|
||||
|
||||
e->id = idCounter;
|
||||
e->object = std::move(obj);
|
||||
|
||||
size = ++numActive > size ? numActive : size;
|
||||
idCounter = idCounter == 0x7FFF ? 1 : idCounter + 1;
|
||||
|
||||
return {ResultSuccess(), Handle{index, e->id, false}};
|
||||
}
|
||||
|
||||
Result KHandleTable::Set(SharedPtr<KAutoObject> obj, Handle handle)
|
||||
{
|
||||
if (!handle.IsAliasOrFree() && IsValid(handle)) {
|
||||
std::scoped_lock guard{spinlock};
|
||||
entries[handle.index].object = std::move(obj);
|
||||
return ResultSuccess();
|
||||
} else {
|
||||
return ResultKernelInvalidHandle();
|
||||
}
|
||||
}
|
||||
|
||||
void KHandleTable::Destroy()
|
||||
{
|
||||
spinlock.lock();
|
||||
u16 capa = capacity;
|
||||
capacity = 0;
|
||||
firstFreeIndex = 0;
|
||||
spinlock.unlock();
|
||||
|
||||
for (u16 i = 0; i < capa; i++) {
|
||||
entries[i].object = nullptr;
|
||||
entries[i].id = -(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
KHandleTable::KHandleTable(size_t capacity_) : capacity((u16)capacity_)
|
||||
{
|
||||
// Note: caller should check the > case, and return an error in that case!
|
||||
capacity = capacity > capacityLimit || capacity == 0 ? (u16)capacityLimit : capacity;
|
||||
|
||||
u16 capa = capacity;
|
||||
Destroy();
|
||||
capacity = capa;
|
||||
}*/
|
||||
|
||||
KHandleTable::~KHandleTable()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
}
|
||||
25
mesosphere/source/processes/KLightClientSession.cpp
Normal file
25
mesosphere/source/processes/KLightClientSession.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <mesosphere/processes/KLightSession.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KLightClientSession::~KLightClientSession()
|
||||
{
|
||||
parent->Terminate(false);
|
||||
}
|
||||
|
||||
Result KLightClientSession::SendSyncRequest(LightSessionRequest *request)
|
||||
{
|
||||
KScopedCriticalSection critsec{};
|
||||
Result res;
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
curThread->SetCurrentLightSessionRequest(request);
|
||||
curThread->ClearSync();
|
||||
res = parent->server.HandleSyncRequest(*curThread);
|
||||
return res.IsSuccess() ? curThread->GetSyncResult() : res;
|
||||
}
|
||||
|
||||
}
|
||||
141
mesosphere/source/processes/KLightServerSession.cpp
Normal file
141
mesosphere/source/processes/KLightServerSession.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include <mesosphere/processes/KLightSession.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KLightServerSession::~KLightServerSession()
|
||||
{
|
||||
Terminate(true);
|
||||
}
|
||||
|
||||
void KLightServerSession::Terminate(bool fromServer)
|
||||
{
|
||||
SharedPtr<KThread> curSender{std::move(currentSender)};
|
||||
{
|
||||
KScopedCriticalSection critsec{};
|
||||
if (fromServer) {
|
||||
parent->isServerAlive = false; // buggy in official kernel -- where it sets it outside of critical section
|
||||
} else {
|
||||
parent->isClientAlive = false;
|
||||
}
|
||||
if (curSender != nullptr) {
|
||||
if (!curSender->IsDying()) {
|
||||
curSender->ResumeFromKernelSync(ResultKernelConnectionClosed());
|
||||
}
|
||||
currentSender = nullptr;
|
||||
currentReceiver = nullptr;
|
||||
}
|
||||
|
||||
for (auto &&sender : senderThreads) {
|
||||
if (!sender.IsDying()) {
|
||||
sender.ResumeFromKernelSync(ResultKernelConnectionClosed()); //TODO check
|
||||
}
|
||||
}
|
||||
|
||||
KThread::ResumeAllFromKernelSync(receiverThreads);
|
||||
}
|
||||
}
|
||||
|
||||
Result KLightServerSession::HandleSyncRequest(KThread &sender)
|
||||
{
|
||||
if (!parent->isClientAlive || !parent->isServerAlive) {
|
||||
return ResultKernelConnectionClosed();
|
||||
}
|
||||
|
||||
if (!sender.WaitForKernelSync(senderThreads)) {
|
||||
return ResultKernelThreadTerminating();
|
||||
}
|
||||
|
||||
if (currentSender != nullptr || receiverThreads.empty()) {
|
||||
// Nothing more to do if a request is being handled or if there's no receiver yet.
|
||||
return ResultSuccess();
|
||||
} else {
|
||||
// Otherwise, wake once receiver.
|
||||
receiverThreads.front().ResumeFromKernelSync();
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
Result KLightServerSession::ReplyAndReceive(LightSessionRequest *request)
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
curThread->SetCurrentLightSessionRequest(request);
|
||||
|
||||
if (request->cmdId < 0) {
|
||||
// Reply
|
||||
SharedPtr<KThread> curSender{};
|
||||
{
|
||||
KScopedCriticalSection critsec{};
|
||||
if (!parent->isClientAlive || !parent->isServerAlive) {
|
||||
return ResultKernelConnectionClosed();
|
||||
}
|
||||
if (currentSender == nullptr || currentReceiver != curThread) {
|
||||
return ResultKernelInvalidState();
|
||||
}
|
||||
|
||||
curSender = std::move(currentSender);
|
||||
if (!curSender->IsDying()) {
|
||||
*curSender->GetCurrentLightSessionRequest() = *curThread->GetCurrentLightSessionRequest();
|
||||
curSender->ResumeFromKernelSync();
|
||||
}
|
||||
currentSender = nullptr;
|
||||
currentReceiver = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Receive
|
||||
KCriticalSection &critsec = KScheduler::GetCriticalSection();
|
||||
std::scoped_lock criticalSection{critsec};
|
||||
bool waitedForSync = false;
|
||||
|
||||
// If there's already one receiver, return an error
|
||||
if (!receiverThreads.empty()) {
|
||||
return ResultKernelInvalidState();
|
||||
}
|
||||
|
||||
while (currentReceiver == nullptr) {
|
||||
if (waitedForSync) {
|
||||
curThread->SetWaitingSync(false);
|
||||
}
|
||||
|
||||
if (!parent->isClientAlive || !parent->isServerAlive) {
|
||||
return ResultKernelConnectionClosed();
|
||||
}
|
||||
|
||||
// Try to see if we can do sync immediately, otherwise wait until later...
|
||||
if (currentSender == nullptr && !senderThreads.empty()) {
|
||||
// Do the sync.
|
||||
currentSender = &senderThreads.front();
|
||||
currentReceiver = curThread;
|
||||
*curThread->GetCurrentLightSessionRequest() = *currentSender->GetCurrentLightSessionRequest();
|
||||
return ResultSuccess();
|
||||
} else {
|
||||
// We didn't get to sync, we need to wait.
|
||||
if (!curThread->WaitForKernelSync(receiverThreads)) {
|
||||
return ResultKernelThreadTerminating();
|
||||
}
|
||||
|
||||
if (curThread->IsSyncCancelled()) {
|
||||
curThread->ResumeFromKernelSync();
|
||||
curThread->SetSyncCancelled(false);
|
||||
return ResultKernelCancelled();
|
||||
}
|
||||
|
||||
// Wait NOW.
|
||||
critsec.unlock();
|
||||
critsec.lock();
|
||||
waitedForSync = true;
|
||||
if (receiverThreads.empty()) {
|
||||
return ResultKernelInvalidState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ResultKernelInvalidState();
|
||||
}
|
||||
|
||||
}
|
||||
37
mesosphere/source/processes/KLightSession.cpp
Normal file
37
mesosphere/source/processes/KLightSession.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <mesosphere/processes/KLightSession.hpp>
|
||||
#include <mesosphere/processes/KPort.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KLightSession::~KLightSession()
|
||||
{
|
||||
}
|
||||
|
||||
Result KLightSession::Initialize(KPort *parentPort)
|
||||
{
|
||||
SetClientServerParent();
|
||||
isClientAlive = true;
|
||||
isServerAlive = true;
|
||||
|
||||
SetResourceOwner(KCoreContext::GetCurrentInstance().GetCurrentProcess());
|
||||
|
||||
if (parentPort == nullptr) {
|
||||
return ResultSuccess();
|
||||
} else {
|
||||
// Another difference with official kernel: if adding the session fails, the session isn't added to allocator set (since it'll be deleted right away).
|
||||
Result res = parentPort->AddLightServerSession(server);
|
||||
if (res.IsSuccess()) {
|
||||
client.parentClientPort = &parentPort->GetClient();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
void KLightSession::Terminate(bool fromServer)
|
||||
{
|
||||
server.Terminate(fromServer);
|
||||
}
|
||||
|
||||
}
|
||||
35
mesosphere/source/processes/KPort.cpp
Normal file
35
mesosphere/source/processes/KPort.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <mesosphere/processes/KPort.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KPort::~KPort()
|
||||
{
|
||||
}
|
||||
|
||||
Result KPort::Initialize(int maxSessions, bool isLight)
|
||||
{
|
||||
SetClientServerParent();
|
||||
isClientAlive = true;
|
||||
isServerAlive = true;
|
||||
|
||||
client.maxSessions = maxSessions;
|
||||
this->isLight = isLight;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KPort::AddLightServerSession(KLightServerSession &lightServerSession)
|
||||
{
|
||||
KScopedCriticalSection critsec{};
|
||||
if (isClientAlive || isServerAlive) {
|
||||
return ResultKernelConnectionRefused();
|
||||
} else {
|
||||
return server.AddLightServerSession(lightServerSession);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
58
mesosphere/source/processes/KProcess.cpp
Normal file
58
mesosphere/source/processes/KProcess.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <mesosphere/processes/KProcess.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/kresources/KResourceLimit.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
void KProcess::SetLastThreadAndIdleSelectionCount(KThread *thread, ulong idleSelectionCount)
|
||||
{
|
||||
lastThreads[thread->GetCurrentCoreId()] = thread;
|
||||
lastIdleSelectionCount[thread->GetCurrentCoreId()] = idleSelectionCount;
|
||||
}
|
||||
|
||||
bool KProcess::IsSignaled() const
|
||||
{
|
||||
return stateChanged;
|
||||
}
|
||||
|
||||
void KProcess::SetDebug(KDebug *debug)
|
||||
{
|
||||
this->debug = debug;
|
||||
if (state != State::DebugSuspended) {
|
||||
state = state == State::Created ? State::CreatedAttached : State::DebugSuspended;
|
||||
stateChanged = true;
|
||||
NotifyWaiters();
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::ClearDebug(KProcess::State attachState)
|
||||
{
|
||||
debug = nullptr;
|
||||
State oldState = state;
|
||||
if (state == State::StartedAttached || state == State::DebugSuspended) {
|
||||
state = attachState == State::Created ? State::Started : attachState; // Restore the old state
|
||||
} else if (state == State::CreatedAttached) {
|
||||
state = State::Created;
|
||||
}
|
||||
|
||||
if (state != oldState) {
|
||||
stateChanged = true;
|
||||
NotifyWaiters();
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::SetDebugPauseState(bool pause) {
|
||||
if (pause && state == State::StartedAttached) {
|
||||
state = State::DebugSuspended;
|
||||
} else if (!pause && state == State::DebugSuspended) {
|
||||
state = State::StartedAttached;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
stateChanged = true;
|
||||
NotifyWaiters();
|
||||
}
|
||||
|
||||
}
|
||||
48
mesosphere/source/processes/KReadableEvent.cpp
Normal file
48
mesosphere/source/processes/KReadableEvent.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <mesosphere/processes/KWritableEvent.hpp>
|
||||
#include <mesosphere/processes/KReadableEvent.hpp>
|
||||
#include <mesosphere/processes/KEvent.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
bool KReadableEvent::IsSignaled() const
|
||||
{
|
||||
return this->isSignaled;
|
||||
}
|
||||
|
||||
KReadableEvent::~KReadableEvent()
|
||||
{
|
||||
}
|
||||
|
||||
Result KReadableEvent::Signal()
|
||||
{
|
||||
KScopedCriticalSection criticalSection{};
|
||||
|
||||
if (!this->isSignaled) {
|
||||
this->isSignaled = true;
|
||||
NotifyWaiters();
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KReadableEvent::Clear()
|
||||
{
|
||||
Reset();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KReadableEvent::Reset()
|
||||
{
|
||||
KScopedCriticalSection criticalSection{};
|
||||
|
||||
if (this->isSignaled) {
|
||||
this->isSignaled = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
return ResultKernelInvalidState();
|
||||
}
|
||||
|
||||
}
|
||||
46
mesosphere/source/processes/KServerPort.cpp
Normal file
46
mesosphere/source/processes/KServerPort.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <mesosphere/processes/KPort.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KServerPort::~KServerPort()
|
||||
{
|
||||
KCriticalSection &critsec = KScheduler::GetCriticalSection();
|
||||
critsec.lock();
|
||||
parent->isServerAlive = false;
|
||||
// TODO: normal sessions
|
||||
lightServerSessions.clear_and_dispose(
|
||||
[&critsec](KLightServerSession *s) {
|
||||
critsec.unlock();
|
||||
intrusive_ptr_release(s);
|
||||
critsec.lock();
|
||||
}
|
||||
);
|
||||
critsec.unlock();
|
||||
}
|
||||
|
||||
bool KServerPort::IsSignaled() const
|
||||
{
|
||||
if (!parent->isLight) {
|
||||
return false; // TODO
|
||||
} else {
|
||||
return !lightServerSessions.empty();
|
||||
}
|
||||
}
|
||||
|
||||
Result KServerPort::AddLightServerSession(KLightServerSession &lightServerSession)
|
||||
{
|
||||
KScopedCriticalSection critsec{};
|
||||
bool wasEmpty = lightServerSessions.empty();
|
||||
lightServerSessions.push_back(lightServerSession);
|
||||
if (wasEmpty && !lightServerSessions.empty()) {
|
||||
NotifyWaiters();
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
22
mesosphere/source/processes/KWritableEvent.cpp
Normal file
22
mesosphere/source/processes/KWritableEvent.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <mesosphere/processes/KWritableEvent.hpp>
|
||||
#include <mesosphere/processes/KReadableEvent.hpp>
|
||||
#include <mesosphere/processes/KEvent.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
KWritableEvent::~KWritableEvent()
|
||||
{
|
||||
}
|
||||
|
||||
Result KWritableEvent::Signal()
|
||||
{
|
||||
return client->Signal();
|
||||
}
|
||||
|
||||
Result KWritableEvent::Clear()
|
||||
{
|
||||
return client->Clear();
|
||||
}
|
||||
|
||||
}
|
||||
17
mesosphere/source/test.cpp
Normal file
17
mesosphere/source/test.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <mesosphere/core/make_object.hpp>
|
||||
#include <mesosphere/interrupts/KInterruptEvent.hpp>
|
||||
|
||||
using namespace mesosphere;
|
||||
|
||||
int main(void) {
|
||||
auto [res, h1] = MakeObjectWithHandle<KInterruptEvent>(3, -1);
|
||||
(void)res; (void)h1; //(void)h2;
|
||||
for(;;);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void _start(void) {
|
||||
main();
|
||||
}
|
||||
}
|
||||
42
mesosphere/source/threading/KConditionVariable.cpp
Normal file
42
mesosphere/source/threading/KConditionVariable.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <mesosphere/threading/KConditionVariable.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
void KConditionVariable::wait_until_impl(const KSystemClock::time_point &timeoutPoint) noexcept
|
||||
{
|
||||
// Official kernel counts number of waiters, but that isn't necessary
|
||||
bool hasWaited = false;
|
||||
KThread *currentThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
{
|
||||
KScopedCriticalSection criticalSection{};
|
||||
mutex_.unlock();
|
||||
if (currentThread->WaitForKernelSync(waiterList) && timeoutPoint > KSystemClock::time_point{}) {
|
||||
hasWaited = true;
|
||||
currentThread->SetAlarmTime(timeoutPoint);
|
||||
}
|
||||
}
|
||||
if (hasWaited) {
|
||||
currentThread->ClearAlarm();
|
||||
}
|
||||
mutex_.lock();
|
||||
}
|
||||
|
||||
void KConditionVariable::notify_one() noexcept
|
||||
{
|
||||
KScopedCriticalSection criticalSection{};
|
||||
auto t = waiterList.begin();
|
||||
if (t != waiterList.end()) {
|
||||
t->ResumeFromKernelSync();
|
||||
}
|
||||
}
|
||||
|
||||
void KConditionVariable::notify_all() noexcept
|
||||
{
|
||||
KScopedCriticalSection criticalSection{};
|
||||
KThread::ResumeAllFromKernelSync(waiterList);
|
||||
}
|
||||
|
||||
}
|
||||
45
mesosphere/source/threading/KCriticalSection.cpp
Normal file
45
mesosphere/source/threading/KCriticalSection.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <mesosphere/threading/KCriticalSection.hpp>
|
||||
#include <mesosphere/threading/KScheduler.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
bool KCriticalSection::try_lock()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
if (curThread == lockingThread) {
|
||||
++lockCount;
|
||||
return true;
|
||||
} else if (KInterruptSpinLock<false>::try_lock()) {
|
||||
lockingThread = curThread;
|
||||
lockCount = 1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void KCriticalSection::lock()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
if (curThread == lockingThread) {
|
||||
++lockCount;
|
||||
} else {
|
||||
KInterruptSpinLock<false>::lock();
|
||||
lockingThread = curThread;
|
||||
lockCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void KCriticalSection::unlock()
|
||||
{
|
||||
if (--lockCount == 0) {
|
||||
lockingThread = nullptr;
|
||||
KScheduler::HandleCriticalSectionLeave();
|
||||
} else {
|
||||
KInterruptSpinLock<false>::unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
62
mesosphere/source/threading/KMutex.cpp
Normal file
62
mesosphere/source/threading/KMutex.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <mesosphere/threading/KMutex.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
void KMutex::lock_slow_path(KThread &owner, KThread &requester)
|
||||
{
|
||||
// Requester is currentThread most of (all ?) the time
|
||||
KCriticalSection &critsec = KScheduler::GetCriticalSection();
|
||||
std::scoped_lock criticalSection{critsec};
|
||||
if (KCoreContext::GetCurrentInstance().GetScheduler()->IsActive()) {
|
||||
requester.SetWantedMutex((uiptr)this);
|
||||
owner.AddMutexWaiter(requester);
|
||||
|
||||
// If the requester is/was running, pause it (sets status even if force-paused).
|
||||
requester.RescheduleIfStatusEquals(KThread::SchedulingStatus::Running, KThread::SchedulingStatus::Paused);
|
||||
|
||||
// If the owner is force-paused, temporarily wake it.
|
||||
if (owner.IsForcePaused()) {
|
||||
owner.AdjustScheduling(owner.RevertForcePauseToField());
|
||||
}
|
||||
|
||||
// Commit scheduler changes NOW.
|
||||
critsec.unlock();
|
||||
critsec.lock();
|
||||
|
||||
/*
|
||||
At this point, mutex ownership has been transferred to requester or another thread (false wake).
|
||||
Make sure the requester, now resumed, isn't in any mutex wait list.
|
||||
*/
|
||||
owner.RemoveMutexWaiter(requester);
|
||||
}
|
||||
}
|
||||
|
||||
void KMutex::unlock_slow_path(KThread &owner)
|
||||
{
|
||||
KScopedCriticalSection criticalSection;
|
||||
size_t count;
|
||||
KThread *newOwner = owner.RelinquishMutex(&count, (uiptr)this);
|
||||
native_handle_type newTag;
|
||||
|
||||
if (newOwner != nullptr) {
|
||||
// Wake up new owner
|
||||
newTag = (native_handle_type)newOwner | (count > 1 ? 1 : 0);
|
||||
// Sets status even if force-paused.
|
||||
newOwner->RescheduleIfStatusEquals(KThread::SchedulingStatus::Paused, KThread::SchedulingStatus::Running);
|
||||
} else {
|
||||
// Free the mutex.
|
||||
newTag = 0;
|
||||
}
|
||||
|
||||
// Allow previous owner to get back to forced-sleep, if no other thread wants the kmutexes it is holding.
|
||||
if (!owner.IsDying() && owner.GetNumberOfKMutexWaiters() == 0) {
|
||||
owner.AdjustScheduling(owner.CommitForcePauseToField());
|
||||
}
|
||||
|
||||
tag = newTag;
|
||||
}
|
||||
|
||||
}
|
||||
355
mesosphere/source/threading/KScheduler.cpp
Normal file
355
mesosphere/source/threading/KScheduler.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#include <mesosphere/threading/KScheduler.hpp>
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
namespace {
|
||||
struct MlqTraitsFactory {
|
||||
constexpr KThread::SchedulerValueTraits operator()(size_t i) const
|
||||
{
|
||||
return KThread::SchedulerValueTraits{(uint)i};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
using MlqT = KScheduler::Global::MlqType;
|
||||
|
||||
bool KScheduler::Global::reselectionRequired = false;
|
||||
|
||||
std::array<MlqT, MAX_CORES> KScheduler::Global::scheduledMlqs =
|
||||
detail::MakeArrayWithFactorySequenceOf<MlqT, MlqTraitsFactory, MAX_CORES>(
|
||||
&KThread::GetPriorityOf
|
||||
);
|
||||
|
||||
std::array<MlqT, MAX_CORES> KScheduler::Global::suggestedMlqs =
|
||||
detail::MakeArrayWithFactorySequenceOf<MlqT, MlqTraitsFactory, MAX_CORES>(
|
||||
&KThread::GetPriorityOf
|
||||
);
|
||||
|
||||
|
||||
void KScheduler::Global::SetThreadRunning(KThread &thread)
|
||||
{
|
||||
ApplyReschedulingOperation([](MlqT &mlq, KThread &t){ mlq.add(t); }, thread);
|
||||
}
|
||||
|
||||
void KScheduler::Global::SetThreadPaused(KThread &thread)
|
||||
{
|
||||
ApplyReschedulingOperation([](MlqT &mlq, KThread &t){ mlq.remove(t); }, thread);
|
||||
}
|
||||
|
||||
void KScheduler::Global::AdjustThreadPriorityChanged(KThread &thread, uint oldPrio, bool isCurrentThread)
|
||||
{
|
||||
ApplyReschedulingOperation(
|
||||
[oldPrio, isCurrentThread](MlqT &mlq, KThread &t){
|
||||
mlq.adjust(t, oldPrio, isCurrentThread);
|
||||
}, thread);
|
||||
}
|
||||
|
||||
void KScheduler::Global::AdjustThreadAffinityChanged(KThread &thread, int oldCoreId, u64 oldAffinityMask)
|
||||
{
|
||||
int newCoreId = thread.GetCurrentCoreId();
|
||||
u64 newAffinityMask = thread.GetAffinityMask();
|
||||
|
||||
ApplyReschedulingOperationImpl([](MlqT &mlq, KThread &t){ mlq.remove(t); }, thread, oldCoreId, oldAffinityMask);
|
||||
ApplyReschedulingOperationImpl([](MlqT &mlq, KThread &t){ mlq.add(t); }, thread, newCoreId, newAffinityMask);
|
||||
|
||||
thread.IncrementSchedulerOperationCount();
|
||||
reselectionRequired = true;
|
||||
}
|
||||
|
||||
void KScheduler::Global::TransferThreadToCore(KThread &thread, int coreId)
|
||||
{
|
||||
int currentCoreId = thread.GetCurrentCoreId();
|
||||
|
||||
if (currentCoreId != coreId) {
|
||||
if (currentCoreId != -1) {
|
||||
scheduledMlqs[currentCoreId].transferToBack(thread, suggestedMlqs[currentCoreId]);
|
||||
}
|
||||
|
||||
if (coreId != -1) {
|
||||
suggestedMlqs[coreId].transferToFront(thread, scheduledMlqs[coreId]);
|
||||
}
|
||||
}
|
||||
|
||||
thread.SetCurrentCoreId(coreId);
|
||||
}
|
||||
|
||||
void KScheduler::Global::AskForReselectionOrMarkRedundant(KThread *currentThread, KThread *winner)
|
||||
{
|
||||
if (currentThread == winner) {
|
||||
// Nintendo (not us) has a nullderef bug on currentThread->owner, but which is never triggered.
|
||||
currentThread->SetRedundantSchedulerOperation();
|
||||
} else {
|
||||
reselectionRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
KThread *KScheduler::Global::PickOneSuggestedThread(const std::array<KThread *, MAX_CORES> &curThreads,
|
||||
uint coreId, bool compareTime, bool allowSecondPass, uint maxPrio, uint minPrio) {
|
||||
if (minPrio < maxPrio) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto hasWorseTime = [coreId, minPrio, compareTime](const KThread &t) {
|
||||
if (!compareTime || scheduledMlqs[coreId].size(minPrio) <= 1 || t.GetPriority() < minPrio) {
|
||||
return false;
|
||||
} else {
|
||||
// Condition means the thread *it would have been scheduled again after the thread
|
||||
return t.GetLastScheduledTime() > scheduledMlqs[coreId].front(minPrio).GetLastScheduledTime();
|
||||
}
|
||||
};
|
||||
|
||||
std::array<uint, MAX_CORES> secondPassCores;
|
||||
size_t numSecondPassCores = 0;
|
||||
|
||||
auto it = std::find_if(
|
||||
suggestedMlqs[coreId].begin(maxPrio),
|
||||
suggestedMlqs[coreId].end(minPrio),
|
||||
[&hasWorseTime, &secondPassCores, &numSecondPassCores, &curThreads](const KThread &t) {
|
||||
int srcCoreId = t.GetCurrentCoreId();
|
||||
//bool worseTime = compareTime && hasWorseTime(t);
|
||||
// break if hasWorse time too
|
||||
if (srcCoreId >= 0) {
|
||||
bool srcHasEphemeralKernThread = scheduledMlqs[srcCoreId].highestPrioritySet() < minRegularPriority;
|
||||
bool isSrcCurT = &t == curThreads[srcCoreId];
|
||||
if (isSrcCurT) {
|
||||
secondPassCores[numSecondPassCores++] = (uint)srcCoreId;
|
||||
}
|
||||
|
||||
// Note, if checkTime official kernel breaks if srcHasEphemeralKernThread
|
||||
// I believe this is a bug
|
||||
if(srcHasEphemeralKernThread || isSrcCurT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
if (it != suggestedMlqs[coreId].end(minPrio) && (!compareTime || !hasWorseTime(*it))) {
|
||||
return &*it;
|
||||
} else if (allowSecondPass) {
|
||||
// Allow to re-pick a selected thread about to be current, if it doesn't make the core idle
|
||||
auto srcCoreIdPtr = std::find_if(
|
||||
secondPassCores.cbegin(),
|
||||
secondPassCores.cbegin() + numSecondPassCores,
|
||||
[](uint id) {
|
||||
return scheduledMlqs[id].highestPrioritySet() >= minRegularPriority && scheduledMlqs[id].size() > 1;
|
||||
}
|
||||
);
|
||||
|
||||
return srcCoreIdPtr == secondPassCores.cbegin() + numSecondPassCores ? nullptr : &scheduledMlqs[*srcCoreIdPtr].front();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::Global::YieldThread(KThread ¤tThread)
|
||||
{
|
||||
// Note: caller should use critical section, etc.
|
||||
kassert(currentThread.GetCurrentCoreId() >= 0);
|
||||
uint coreId = (uint)currentThread.GetCurrentCoreId();
|
||||
uint priority = currentThread.GetPriority();
|
||||
|
||||
// Yield the thread
|
||||
scheduledMlqs[coreId].yield(currentThread);
|
||||
currentThread.IncrementSchedulerOperationCount();
|
||||
|
||||
KThread *winner = &scheduledMlqs[coreId].front(priority);
|
||||
AskForReselectionOrMarkRedundant(¤tThread, winner);
|
||||
}
|
||||
|
||||
void KScheduler::Global::YieldThreadAndBalanceLoad(KThread ¤tThread)
|
||||
{
|
||||
// Note: caller should check if !currentThread.IsSchedulerOperationRedundant and use critical section, etc.
|
||||
kassert(currentThread.GetCurrentCoreId() >= 0);
|
||||
uint coreId = (uint)currentThread.GetCurrentCoreId();
|
||||
uint priority = currentThread.GetPriority();
|
||||
|
||||
std::array<KThread *, MAX_CORES> curThreads;
|
||||
for (uint i = 0; i < MAX_CORES; i++) {
|
||||
curThreads[i] = scheduledMlqs[i].empty() ? nullptr : &scheduledMlqs[i].front();
|
||||
}
|
||||
|
||||
// Yield the thread
|
||||
scheduledMlqs[coreId].yield(currentThread);
|
||||
currentThread.IncrementSchedulerOperationCount();
|
||||
|
||||
KThread *winner = PickOneSuggestedThread(curThreads, coreId, true, false, 0, priority);
|
||||
|
||||
if (winner != nullptr) {
|
||||
TransferThreadToCore(*winner, coreId);
|
||||
winner->IncrementSchedulerOperationCount();
|
||||
currentThread.SetRedundantSchedulerOperation();
|
||||
} else {
|
||||
winner = &scheduledMlqs[coreId].front(priority);
|
||||
}
|
||||
|
||||
AskForReselectionOrMarkRedundant(¤tThread, winner);
|
||||
}
|
||||
|
||||
void KScheduler::Global::YieldThreadAndWaitForLoadBalancing(KThread ¤tThread)
|
||||
{
|
||||
// Note: caller should check if !currentThread.IsSchedulerOperationRedundant and use critical section, etc.
|
||||
KThread *winner = nullptr;
|
||||
kassert(currentThread.GetCurrentCoreId() >= 0);
|
||||
uint coreId = (uint)currentThread.GetCurrentCoreId();
|
||||
|
||||
// Remove the thread from its scheduled mlq, put it on the corresponding "suggested" one instead
|
||||
TransferThreadToCore(currentThread, -1);
|
||||
currentThread.IncrementSchedulerOperationCount();
|
||||
|
||||
// If the core is idle, perform load balancing, excluding the threads that have just used this function...
|
||||
if (scheduledMlqs[coreId].empty()) {
|
||||
// Here, "curThreads" is calculated after the ""yield"", unlike yield -1
|
||||
std::array<KThread *, MAX_CORES> curThreads;
|
||||
for (uint i = 0; i < MAX_CORES; i++) {
|
||||
curThreads[i] = scheduledMlqs[i].empty() ? nullptr : &scheduledMlqs[i].front();
|
||||
}
|
||||
|
||||
KThread *winner = PickOneSuggestedThread(curThreads, coreId, false);
|
||||
|
||||
if (winner != nullptr) {
|
||||
TransferThreadToCore(*winner, coreId);
|
||||
winner->IncrementSchedulerOperationCount();
|
||||
} else {
|
||||
winner = ¤tThread;
|
||||
}
|
||||
}
|
||||
|
||||
AskForReselectionOrMarkRedundant(¤tThread, winner);
|
||||
}
|
||||
|
||||
void KScheduler::Global::YieldPreemptThread(KThread ¤tKernelHandlerThread, uint coreId, uint maxPrio)
|
||||
{
|
||||
if (!scheduledMlqs[coreId].empty(maxPrio)) {
|
||||
// Yield the first thread in the level queue
|
||||
scheduledMlqs[coreId].front(maxPrio).IncrementSchedulerOperationCount();
|
||||
scheduledMlqs[coreId].yield(maxPrio);
|
||||
if (scheduledMlqs[coreId].size() > 1) {
|
||||
scheduledMlqs[coreId].front(maxPrio).IncrementSchedulerOperationCount();
|
||||
}
|
||||
}
|
||||
|
||||
// Here, "curThreads" is calculated after the forced yield, unlike yield -1
|
||||
std::array<KThread *, MAX_CORES> curThreads;
|
||||
for (uint i = 0; i < MAX_CORES; i++) {
|
||||
curThreads[i] = scheduledMlqs[i].empty() ? nullptr : &scheduledMlqs[i].front();
|
||||
}
|
||||
|
||||
KThread *winner = PickOneSuggestedThread(curThreads, coreId, true, false, maxPrio, maxPrio);
|
||||
if (winner != nullptr) {
|
||||
TransferThreadToCore(*winner, coreId);
|
||||
winner->IncrementSchedulerOperationCount();
|
||||
}
|
||||
|
||||
for (uint i = 0; i < MAX_CORES; i++) {
|
||||
curThreads[i] = scheduledMlqs[i].empty() ? nullptr : &scheduledMlqs[i].front();
|
||||
}
|
||||
|
||||
// Find first thread which is not the kernel handler thread.
|
||||
auto itFirst = std::find_if(
|
||||
scheduledMlqs[coreId].begin(),
|
||||
scheduledMlqs[coreId].end(),
|
||||
[¤tKernelHandlerThread, coreId](const KThread &t) {
|
||||
return &t != ¤tKernelHandlerThread;
|
||||
}
|
||||
);
|
||||
|
||||
if (itFirst != scheduledMlqs[coreId].end()) {
|
||||
// If under the threshold, do load balancing again
|
||||
winner = PickOneSuggestedThread(curThreads, coreId, true, false, maxPrio, itFirst->GetPriority() - 1);
|
||||
if (winner != nullptr) {
|
||||
TransferThreadToCore(*winner, coreId);
|
||||
winner->IncrementSchedulerOperationCount();
|
||||
}
|
||||
}
|
||||
|
||||
reselectionRequired = true;
|
||||
}
|
||||
|
||||
void KScheduler::Global::SelectThreads()
|
||||
{
|
||||
auto updateThread = [](KThread *thread, KScheduler &sched) {
|
||||
if (thread != sched.selectedThread) {
|
||||
if (thread != nullptr) {
|
||||
thread->IncrementSchedulerOperationCount();
|
||||
thread->UpdateLastScheduledTime();
|
||||
thread->SetProcessLastThreadAndIdleSelectionCount(sched.idleSelectionCount);
|
||||
} else {
|
||||
++sched.idleSelectionCount;
|
||||
}
|
||||
sched.selectedThread = thread;
|
||||
sched.isContextSwitchNeeded = true;
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
};
|
||||
|
||||
// This maintain the "current thread is on front of queue" invariant
|
||||
std::array<KThread *, MAX_CORES> curThreads;
|
||||
for (uint i = 0; i < MAX_CORES; i++) {
|
||||
KScheduler &sched = *KCoreContext::GetInstance(i).GetScheduler();
|
||||
curThreads[i] = scheduledMlqs[i].empty() ? nullptr : &scheduledMlqs[i].front();
|
||||
updateThread(curThreads[i], sched);
|
||||
}
|
||||
|
||||
// Do some load-balancing. Allow second pass.
|
||||
std::array<KThread *, MAX_CORES> curThreads2 = curThreads;
|
||||
for (uint i = 0; i < MAX_CORES; i++) {
|
||||
if (scheduledMlqs[i].empty()) {
|
||||
KThread *winner = PickOneSuggestedThread(curThreads2, i, false, true);
|
||||
if (winner != nullptr) {
|
||||
curThreads2[i] = winner;
|
||||
TransferThreadToCore(*winner, i);
|
||||
winner->IncrementSchedulerOperationCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See which to-be-current threads have changed & update accordingly
|
||||
for (uint i = 0; i < MAX_CORES; i++) {
|
||||
KScheduler &sched = *KCoreContext::GetInstance(i).GetScheduler();
|
||||
if (curThreads2[i] != curThreads[i]) {
|
||||
updateThread(curThreads2[i], sched);
|
||||
}
|
||||
}
|
||||
reselectionRequired = false;
|
||||
}
|
||||
|
||||
KCriticalSection KScheduler::criticalSection{};
|
||||
|
||||
void KScheduler::YieldCurrentThread()
|
||||
{
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
cctx.GetScheduler()->DoYieldOperation(Global::YieldThread, *cctx.GetCurrentThread());
|
||||
}
|
||||
|
||||
void KScheduler::YieldCurrentThreadAndBalanceLoad()
|
||||
{
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
cctx.GetScheduler()->DoYieldOperation(Global::YieldThreadAndBalanceLoad, *cctx.GetCurrentThread());
|
||||
}
|
||||
|
||||
void KScheduler::YieldCurrentThreadAndWaitForLoadBalancing()
|
||||
{
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
cctx.GetScheduler()->DoYieldOperation(Global::YieldThreadAndWaitForLoadBalancing, *cctx.GetCurrentThread());
|
||||
}
|
||||
|
||||
void KScheduler::HandleCriticalSectionLeave()
|
||||
{
|
||||
if (KScheduler::Global::reselectionRequired) {
|
||||
KScheduler::Global::SelectThreads();
|
||||
}
|
||||
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
// TODO: check which cores needs ctx switches, sent interrupts and/or ctx switch ourselves
|
||||
}
|
||||
|
||||
}
|
||||
351
mesosphere/source/threading/KThread.cpp
Normal file
351
mesosphere/source/threading/KThread.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/processes/KProcess.hpp>
|
||||
#include <mesosphere/threading/KScopedCriticalSection.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
void KThread::OnAlarm()
|
||||
{
|
||||
CancelKernelSync();
|
||||
}
|
||||
|
||||
bool KThread::IsSchedulerOperationRedundant() const
|
||||
{
|
||||
return owner != nullptr && owner->GetSchedulerOperationCount() == redundantSchedulerOperationCount;
|
||||
}
|
||||
|
||||
void KThread::IncrementSchedulerOperationCount()
|
||||
{
|
||||
if (owner != nullptr) {
|
||||
owner->IncrementSchedulerOperationCount();
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::SetRedundantSchedulerOperation()
|
||||
{
|
||||
redundantSchedulerOperationCount = owner != nullptr ? owner->GetSchedulerOperationCount() : redundantSchedulerOperationCount;
|
||||
}
|
||||
|
||||
void KThread::SetProcessLastThreadAndIdleSelectionCount(ulong idleSelectionCount)
|
||||
{
|
||||
if (owner != nullptr) {
|
||||
owner->SetLastThreadAndIdleSelectionCount(this, idleSelectionCount);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::AdjustScheduling(ushort oldMaskFull)
|
||||
{
|
||||
if (currentSchedMaskFull == oldMaskFull) {
|
||||
return;
|
||||
} else if (CompareSchedulingStatusFull(oldMaskFull, SchedulingStatus::Running)) {
|
||||
KScheduler::Global::SetThreadPaused(*this);
|
||||
} else if (CompareSchedulingStatusFull(SchedulingStatus::Running)) {
|
||||
KScheduler::Global::SetThreadRunning(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::Reschedule(KThread::SchedulingStatus newStatus)
|
||||
{
|
||||
//KScopedCriticalSection criticalSection{};
|
||||
// TODO check the above ^
|
||||
AdjustScheduling(SetSchedulingStatusField(newStatus));
|
||||
}
|
||||
|
||||
void KThread::RescheduleIfStatusEquals(SchedulingStatus expectedStatus, SchedulingStatus newStatus)
|
||||
{
|
||||
if(GetSchedulingStatus() == expectedStatus) {
|
||||
Reschedule(newStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::AddForcePauseReason(KThread::ForcePauseReason reason)
|
||||
{
|
||||
KScopedCriticalSection criticalSection;
|
||||
|
||||
if (!IsDying()) {
|
||||
AddForcePauseReasonToField(reason);
|
||||
if (numKernelMutexWaiters == 0) {
|
||||
AdjustScheduling(CommitForcePauseToField());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::RemoveForcePauseReason(KThread::ForcePauseReason reason)
|
||||
{
|
||||
KScopedCriticalSection criticalSection;
|
||||
|
||||
if (!IsDying()) {
|
||||
RemoveForcePauseReasonToField(reason);
|
||||
if (!IsForcePaused() && numKernelMutexWaiters == 0) {
|
||||
AdjustScheduling(CommitForcePauseToField());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KThread::WaitForKernelSync(KThread::WaitList &waitList)
|
||||
{
|
||||
// Has to be called from critical section
|
||||
currentWaitList = &waitList;
|
||||
Reschedule(SchedulingStatus::Paused);
|
||||
waitList.push_back(*this);
|
||||
if (IsDying()) {
|
||||
// Whoops
|
||||
ResumeFromKernelSync();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KThread::ResumeFromKernelSync()
|
||||
{
|
||||
// Has to be called from critical section
|
||||
currentWaitList->erase(currentWaitList->iterator_to(*this));
|
||||
currentWaitList = nullptr;
|
||||
Reschedule(SchedulingStatus::Running);
|
||||
}
|
||||
|
||||
void KThread::ResumeFromKernelSync(Result res)
|
||||
{
|
||||
// Has to be called from critical section
|
||||
syncResult = res;
|
||||
ResumeFromKernelSync();
|
||||
}
|
||||
|
||||
void KThread::ResumeAllFromKernelSync(KThread::WaitList &waitList)
|
||||
{
|
||||
// Has to be called from critical section
|
||||
waitList.clear_and_dispose(
|
||||
[](KThread *t) {
|
||||
t->currentWaitList = nullptr;
|
||||
t->Reschedule(SchedulingStatus::Running);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void KThread::ResumeAllFromKernelSync(KThread::WaitList &waitList, Result res)
|
||||
{
|
||||
// Has to be called from critical section
|
||||
waitList.clear_and_dispose(
|
||||
[res](KThread *t) {
|
||||
t->syncResult = res;
|
||||
t->currentWaitList = nullptr;
|
||||
t->Reschedule(SchedulingStatus::Running);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
void KThread::CancelKernelSync()
|
||||
{
|
||||
KScopedCriticalSection criticalSection;
|
||||
if (GetSchedulingStatus() == SchedulingStatus::Paused) {
|
||||
// Note: transparent to force-pause
|
||||
if (currentWaitList != nullptr) {
|
||||
ResumeFromKernelSync();
|
||||
} else {
|
||||
Reschedule(SchedulingStatus::Running);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::CancelKernelSync(Result res)
|
||||
{
|
||||
syncResult = res;
|
||||
CancelKernelSync();
|
||||
}
|
||||
|
||||
void KThread::HandleSyncObjectSignaled(KSynchronizationObject *syncObj)
|
||||
{
|
||||
if (GetSchedulingStatus() == SchedulingStatus::Paused) {
|
||||
signaledSyncObject = syncObj;
|
||||
syncResult = ResultSuccess();
|
||||
Reschedule(SchedulingStatus::Running);
|
||||
}
|
||||
}
|
||||
|
||||
Result KThread::WaitSynchronizationImpl(int &outId, KSynchronizationObject **syncObjs, int numSyncObjs, const KSystemClock::time_point &timeoutTime)
|
||||
{
|
||||
KLinkedList<KThread *>::const_iterator nodes[numSyncObjs];
|
||||
|
||||
outId = -1;
|
||||
{
|
||||
KScopedCriticalSection criticalSection;
|
||||
|
||||
// Try to find an already signaled object.
|
||||
if (numSyncObjs >= 1) {
|
||||
KSynchronizationObject **readyFound = std::find_if(
|
||||
syncObjs,
|
||||
syncObjs + numSyncObjs,
|
||||
[](KSynchronizationObject *obj) {
|
||||
return obj->IsSignaled();
|
||||
}
|
||||
);
|
||||
|
||||
outId = readyFound - syncObjs >= numSyncObjs ? -1 : readyFound - syncObjs;
|
||||
}
|
||||
|
||||
if (timeoutTime == KSystemClock::time_point{} && outId == -1) {
|
||||
return ResultKernelTimedOut();
|
||||
}
|
||||
if (IsDying()) {
|
||||
return ResultKernelThreadTerminating();
|
||||
}
|
||||
if (isSyncCancelled) {
|
||||
return ResultKernelCancelled();
|
||||
}
|
||||
|
||||
for (int i = 0; i < numSyncObjs; i++) {
|
||||
nodes[i] = syncObjs[i]->AddWaiter(*this);
|
||||
}
|
||||
|
||||
isWaitingSync = true;
|
||||
signaledSyncObject = nullptr;
|
||||
syncResult = ResultKernelTimedOut();
|
||||
|
||||
Reschedule(SchedulingStatus::Paused);
|
||||
if (timeoutTime > KSystemClock::time_point{}) {
|
||||
SetAlarmTime(timeoutTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Now waiting...
|
||||
|
||||
{
|
||||
KScopedCriticalSection criticalSection;
|
||||
|
||||
isWaitingSync = false;
|
||||
if (timeoutTime > KSystemClock::time_point{}) {
|
||||
ClearAlarm();
|
||||
}
|
||||
|
||||
for (int i = 0; i < numSyncObjs; i++) {
|
||||
syncObjs[i]->RemoveWaiter(nodes[i]);
|
||||
if (syncObjs[i] == signaledSyncObject) {
|
||||
outId = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return syncResult;
|
||||
}
|
||||
|
||||
void KThread::AddToMutexWaitList(KThread &thread)
|
||||
{
|
||||
// TODO: check&increment numKernelMutexWaiters
|
||||
// Ordered list insertion
|
||||
auto it = std::find_if(
|
||||
mutexWaitList.begin(),
|
||||
mutexWaitList.end(),
|
||||
[&thread](const KThread &t) {
|
||||
return t.GetPriority() > thread.GetPriority();
|
||||
}
|
||||
);
|
||||
|
||||
if (it != mutexWaitList.end()) {
|
||||
mutexWaitList.insert(it, thread);
|
||||
} else {
|
||||
mutexWaitList.push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
KThread::MutexWaitList::iterator KThread::RemoveFromMutexWaitList(KThread::MutexWaitList::const_iterator it)
|
||||
{
|
||||
// TODO: check&decrement numKernelMutexWaiters
|
||||
return mutexWaitList.erase(it);
|
||||
}
|
||||
|
||||
void KThread::RemoveFromMutexWaitList(const KThread &t)
|
||||
{
|
||||
RemoveFromMutexWaitList(mutexWaitList.iterator_to(t));
|
||||
}
|
||||
|
||||
void KThread::InheritDynamicPriority()
|
||||
{
|
||||
/*
|
||||
Do priority inheritance
|
||||
Since we're maybe changing the priority of the thread,
|
||||
we must go through the entire mutex owner chain.
|
||||
The invariant must be preserved:
|
||||
A thread holding a mutex must have a higher-or-same priority than
|
||||
all threads waiting for it to release the mutex.
|
||||
*/
|
||||
|
||||
for (KThread *t = this; t != nullptr; t = t->wantedMutexOwner) {
|
||||
uint newPrio, oldPrio = priority;
|
||||
if (!mutexWaitList.empty() && mutexWaitList.front().priority < basePriority) {
|
||||
newPrio = mutexWaitList.front().priority;
|
||||
} else {
|
||||
newPrio = basePriority;
|
||||
}
|
||||
|
||||
if (newPrio == oldPrio) {
|
||||
break;
|
||||
} else {
|
||||
// Update everything that depends on dynamic priority:
|
||||
|
||||
// TODO update condvar
|
||||
// TODO update ctr arbiter
|
||||
priority = newPrio;
|
||||
// TODO update condvar
|
||||
// TODO update ctr arbiter
|
||||
if (CompareSchedulingStatusFull(SchedulingStatus::Running)) {
|
||||
KScheduler::Global::AdjustThreadPriorityChanged(*this, oldPrio, this == KCoreContext::GetCurrentInstance().GetCurrentThread());
|
||||
}
|
||||
|
||||
if (wantedMutexOwner != nullptr) {
|
||||
wantedMutexOwner->RemoveFromMutexWaitList(*this);
|
||||
wantedMutexOwner->AddToMutexWaitList(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::AddMutexWaiter(KThread &waiter)
|
||||
{
|
||||
AddToMutexWaitList(waiter);
|
||||
InheritDynamicPriority();
|
||||
}
|
||||
|
||||
void KThread::RemoveMutexWaiter(KThread &waiter)
|
||||
{
|
||||
RemoveFromMutexWaitList(waiter);
|
||||
InheritDynamicPriority();
|
||||
}
|
||||
|
||||
KThread *KThread::RelinquishMutex(size_t *count, uiptr mutexAddr)
|
||||
{
|
||||
KThread *newOwner = nullptr;
|
||||
*count = 0;
|
||||
|
||||
// First in list wanting mutexAddr becomes owner, the rest is transferred
|
||||
for (auto it = mutexWaitList.begin(); it != mutexWaitList.end(); ) {
|
||||
if (it->wantedMutex != mutexAddr) {
|
||||
++it;
|
||||
continue;
|
||||
} else {
|
||||
KThread &t = *it;
|
||||
++(*count);
|
||||
it = RemoveFromMutexWaitList(it);
|
||||
if (newOwner == nullptr) {
|
||||
newOwner = &t;
|
||||
} else {
|
||||
newOwner->AddToMutexWaitList(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mutex waiters list have changed
|
||||
InheritDynamicPriority();
|
||||
if (newOwner != nullptr) {
|
||||
newOwner->InheritDynamicPriority();
|
||||
}
|
||||
|
||||
return newOwner;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user