Merge pull request 'Add a basic scheduler with threads' (#18) from threads into restart
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #18
This commit is contained in:
commit
416e96c4d5
@ -12,6 +12,8 @@ set(SOURCES
|
||||
src/boot/Init.cpp
|
||||
src/arch/Serial.cpp
|
||||
src/arch/Timer.cpp
|
||||
src/thread/Thread.cpp
|
||||
src/thread/Scheduler.cpp
|
||||
)
|
||||
|
||||
if("${ARCH}" MATCHES "x86_64")
|
||||
@ -22,6 +24,7 @@ if("${ARCH}" MATCHES "x86_64")
|
||||
src/arch/x86_64/MMU.cpp
|
||||
src/arch/x86_64/CPU.cpp
|
||||
src/arch/x86_64/Timer.cpp
|
||||
src/arch/x86_64/Thread.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -13,11 +13,11 @@ namespace CPU
|
||||
|
||||
[[noreturn]] void efficient_halt();
|
||||
|
||||
[[noreturn]] void idle_loop();
|
||||
|
||||
void switch_kernel_stack(u64 top);
|
||||
|
||||
void enable_interrupts();
|
||||
void disable_interrupts();
|
||||
void wait_for_interrupt();
|
||||
}
|
||||
|
||||
extern "C" void kernel_yield();
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
#include "arch/Serial.h"
|
||||
#include <luna/Result.h>
|
||||
#include <luna/Format.h>
|
||||
#include <luna/Result.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
namespace Serial
|
||||
|
@ -58,6 +58,11 @@ namespace Timer
|
||||
timer_ticks++;
|
||||
}
|
||||
|
||||
usize raw_ticks()
|
||||
{
|
||||
return timer_ticks;
|
||||
}
|
||||
|
||||
usize ticks()
|
||||
{
|
||||
return ticks_ms() / 1000;
|
||||
@ -126,4 +131,10 @@ namespace Timer
|
||||
boot_timestamp = bootloader_time_to_unix(bootboot.datetime);
|
||||
arch_init();
|
||||
}
|
||||
}
|
||||
|
||||
bool should_invoke_scheduler()
|
||||
{
|
||||
// FIXME: Modulo is SLOW. We're calling this every tick.
|
||||
return (timer_ticks % ARCH_TIMER_FREQ) == 0;
|
||||
}
|
@ -34,4 +34,6 @@ namespace Timer
|
||||
|
||||
void arch_init();
|
||||
void init();
|
||||
}
|
||||
}
|
||||
|
||||
bool should_invoke_scheduler();
|
@ -48,12 +48,7 @@ load_tr:
|
||||
ltr ax
|
||||
ret
|
||||
|
||||
global switch_task
|
||||
switch_task:
|
||||
cli
|
||||
.loop:
|
||||
hlt
|
||||
jmp .loop
|
||||
extern switch_task
|
||||
|
||||
global kernel_yield
|
||||
kernel_yield:
|
||||
|
@ -1,14 +1,15 @@
|
||||
#include "arch/CPU.h"
|
||||
#include "arch/x86_64/CPU.h"
|
||||
#include "Log.h"
|
||||
#include "arch/Timer.h"
|
||||
#include "arch/x86_64/CPU.h"
|
||||
#include "arch/x86_64/IO.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <cpuid.h>
|
||||
#include <luna/Check.h>
|
||||
#include <luna/Result.h>
|
||||
#include <luna/String.h>
|
||||
#include <luna/SystemError.h>
|
||||
#include <luna/Types.h>
|
||||
#include <luna/Check.h>
|
||||
#include <luna/Result.h>
|
||||
|
||||
extern "C" void enable_sse();
|
||||
extern "C" void enable_write_protect();
|
||||
@ -336,6 +337,7 @@ extern "C" void arch_interrupt_entry(Registers* regs)
|
||||
else if (regs->isr == 32)
|
||||
{
|
||||
Timer::tick();
|
||||
if (should_invoke_scheduler()) Scheduler::invoke(regs);
|
||||
pic_eoi(regs);
|
||||
}
|
||||
else
|
||||
@ -430,8 +432,22 @@ namespace CPU
|
||||
goto loop; // Safeguard: if we ever wake up, start our low-power rest again
|
||||
}
|
||||
|
||||
[[noreturn]] void idle_loop()
|
||||
{
|
||||
asm volatile("sti");
|
||||
loop:
|
||||
asm volatile("hlt");
|
||||
goto loop;
|
||||
}
|
||||
|
||||
void switch_kernel_stack(u64 top)
|
||||
{
|
||||
task_state_segment.rsp[0] = top;
|
||||
}
|
||||
}
|
||||
|
||||
// called by kernel_yield
|
||||
extern "C" void switch_task(Registers* regs)
|
||||
{
|
||||
Scheduler::switch_task(regs);
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
#include <luna/Types.h>
|
||||
|
||||
struct Registers // Saved CPU registers for x86-64
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include "arch/MMU.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include <luna/Result.h>
|
||||
#include <luna/String.h>
|
||||
#include <luna/SystemError.h>
|
||||
#include <luna/Result.h>
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
|
50
kernel/src/arch/x86_64/Thread.cpp
Normal file
50
kernel/src/arch/x86_64/Thread.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "thread/Thread.h"
|
||||
#include <luna/String.h>
|
||||
|
||||
bool is_in_kernel(Registers* regs)
|
||||
{
|
||||
return regs->cs == 8;
|
||||
}
|
||||
|
||||
void Thread::set_ip(u64 ip)
|
||||
{
|
||||
regs.rip = ip;
|
||||
}
|
||||
|
||||
u64 Thread::ip()
|
||||
{
|
||||
return regs.rip;
|
||||
}
|
||||
|
||||
void Thread::set_sp(u64 sp)
|
||||
{
|
||||
regs.rsp = sp;
|
||||
}
|
||||
|
||||
u64 Thread::sp()
|
||||
{
|
||||
return regs.rsp;
|
||||
}
|
||||
|
||||
void Thread::init_regs_kernel()
|
||||
{
|
||||
memset(®s, 0, sizeof(Registers));
|
||||
regs.cs = 0x08;
|
||||
regs.ss = 0x10;
|
||||
regs.rflags = 1 << 9; // IF (Interrupt enable flag)
|
||||
}
|
||||
|
||||
void Thread::set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4)
|
||||
{
|
||||
regs.rdi = arg1;
|
||||
regs.rsi = arg2;
|
||||
regs.rdx = arg3;
|
||||
regs.rcx = arg4;
|
||||
}
|
||||
|
||||
void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs)
|
||||
{
|
||||
if (!old_thread->is_idle()) memcpy(&old_thread->regs, regs, sizeof(Registers));
|
||||
|
||||
memcpy(regs, &new_thread->regs, sizeof(Registers));
|
||||
}
|
@ -4,8 +4,8 @@
|
||||
#include "boot/bootboot.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "video/Framebuffer.h"
|
||||
#include <luna/String.h>
|
||||
#include <luna/Result.h>
|
||||
#include <luna/String.h>
|
||||
|
||||
extern const BOOTBOOT bootboot;
|
||||
|
||||
|
@ -1,11 +1,27 @@
|
||||
#include "Log.h"
|
||||
#include "arch/CPU.h"
|
||||
#include "arch/Serial.h"
|
||||
#include "arch/Timer.h"
|
||||
#include "boot/Init.h"
|
||||
#include "config.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include <luna/Units.h>
|
||||
#include "thread/Scheduler.h"
|
||||
#include <luna/Result.h>
|
||||
#include <luna/Units.h>
|
||||
|
||||
void async_thread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Thread* current = Scheduler::current();
|
||||
kinfoln("Ticks: %lu, %lu user, %lu kernel, %lu idle", current->ticks, current->ticks_in_user,
|
||||
current->ticks_in_kernel, Scheduler::idle()->ticks);
|
||||
|
||||
CPU::wait_for_interrupt();
|
||||
|
||||
kernel_sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Result<void> init()
|
||||
{
|
||||
@ -17,10 +33,6 @@ Result<void> init()
|
||||
|
||||
Timer::init();
|
||||
|
||||
CPU::platform_finish_init();
|
||||
|
||||
CPU::enable_interrupts();
|
||||
|
||||
char buffer[64];
|
||||
to_dynamic_unit(MemoryManager::total(), buffer, sizeof(buffer));
|
||||
kinfoln("Total memory: %s", buffer);
|
||||
@ -31,6 +43,14 @@ Result<void> init()
|
||||
to_dynamic_unit(MemoryManager::reserved(), buffer, sizeof(buffer));
|
||||
kinfoln("Reserved memory: %s", buffer);
|
||||
|
||||
Scheduler::init();
|
||||
|
||||
TRY(Scheduler::new_kernel_thread(async_thread));
|
||||
|
||||
CPU::platform_finish_init();
|
||||
|
||||
CPU::enable_interrupts();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -40,5 +60,5 @@ extern "C" [[noreturn]] void _start()
|
||||
Init::early_init();
|
||||
auto rc = init();
|
||||
if (rc.has_error()) kerrorln("Runtime error: %s", rc.error_string());
|
||||
CPU::efficient_halt();
|
||||
CPU::idle_loop();
|
||||
}
|
@ -178,7 +178,7 @@ Result<void*> kmalloc(usize size)
|
||||
heap.append(block);
|
||||
}
|
||||
|
||||
HeapBlock* block = heap.first().value();
|
||||
HeapBlock* block = heap.expect_first();
|
||||
while (block)
|
||||
{
|
||||
// Trying to find a free block...
|
||||
@ -330,7 +330,7 @@ void dump_heap_usage()
|
||||
}
|
||||
usize alloc_total = 0;
|
||||
usize alloc_used = 0;
|
||||
HeapBlock* block = heap.first().value();
|
||||
HeapBlock* block = heap.expect_first();
|
||||
while (block)
|
||||
{
|
||||
if (is_block_free(block))
|
||||
|
@ -21,6 +21,7 @@ namespace KernelVM
|
||||
void init()
|
||||
{
|
||||
g_kernelvm_bitmap.initialize(bitmap_memory, sizeof(bitmap_memory));
|
||||
g_kernelvm_bitmap.clear(false);
|
||||
}
|
||||
|
||||
Result<u64> alloc_one_page()
|
||||
@ -36,7 +37,7 @@ namespace KernelVM
|
||||
return err(ENOMEM);
|
||||
}
|
||||
|
||||
Result<u64> alloc_several_pages(usize count)
|
||||
bool find_several_pages_impl(usize count, u64& start_index)
|
||||
{
|
||||
u64 first_free_index = 0;
|
||||
u64 free_contiguous_pages = 0;
|
||||
@ -56,12 +57,24 @@ namespace KernelVM
|
||||
// Found enough contiguous free pages!!
|
||||
if (free_contiguous_pages == count)
|
||||
{
|
||||
g_used_vm += ARCH_PAGE_SIZE * count;
|
||||
g_kernelvm_bitmap.clear_region(first_free_index, count, true);
|
||||
return KERNEL_VM_RANGE_START + (first_free_index * ARCH_PAGE_SIZE);
|
||||
start_index = first_free_index;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Result<u64> alloc_several_pages(const usize count)
|
||||
{
|
||||
u64 start_index;
|
||||
if (find_several_pages_impl(count, start_index))
|
||||
{
|
||||
g_kernelvm_bitmap.clear_region(start_index, count, true);
|
||||
g_used_vm += ARCH_PAGE_SIZE * count;
|
||||
return KERNEL_VM_RANGE_START + (start_index * ARCH_PAGE_SIZE);
|
||||
}
|
||||
|
||||
return err(ENOMEM);
|
||||
}
|
||||
|
||||
|
170
kernel/src/thread/Scheduler.cpp
Normal file
170
kernel/src/thread/Scheduler.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
#include "thread/Scheduler.h"
|
||||
#include "Log.h"
|
||||
#include "arch/CPU.h"
|
||||
#include "arch/MMU.h"
|
||||
#include "memory/KernelVM.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include <luna/Stack.h>
|
||||
|
||||
static Thread g_idle;
|
||||
static Thread* g_current = nullptr;
|
||||
|
||||
static const usize TICKS_PER_TIMESLICE = 20;
|
||||
|
||||
namespace Scheduler
|
||||
{
|
||||
void init()
|
||||
{
|
||||
g_idle.id = 0;
|
||||
g_idle.init_regs_kernel();
|
||||
g_idle.set_ip((u64)CPU::idle_loop);
|
||||
g_idle.state = ThreadState::Idle;
|
||||
|
||||
g_idle.ticks_left = 1;
|
||||
|
||||
// Map some stack for the idle task
|
||||
u64 idle_stack_vm = KernelVM::alloc_one_page().release_value();
|
||||
MemoryManager::alloc_at(idle_stack_vm, 1, MMU::NoExecute | MMU::ReadWrite).release_value();
|
||||
|
||||
Stack idle_stack{idle_stack_vm, ARCH_PAGE_SIZE};
|
||||
g_idle.set_sp(idle_stack.top());
|
||||
|
||||
kinfoln("CREATED IDLE THREAD: id %lu with ip %lx and sp %lx", g_idle.id, g_idle.ip(), g_idle.sp());
|
||||
|
||||
g_current = &g_idle;
|
||||
}
|
||||
|
||||
Thread* current()
|
||||
{
|
||||
return g_current;
|
||||
}
|
||||
|
||||
Thread* idle()
|
||||
{
|
||||
return &g_idle;
|
||||
}
|
||||
|
||||
Result<void> kernel_thread_alloc_stack_and_append_impl(Thread* thread)
|
||||
{
|
||||
// FIXME: We will leak the thread if VM allocation or alloc_at fail.
|
||||
u64 thread_stack_vm = TRY(KernelVM::alloc_several_pages(4));
|
||||
TRY(MemoryManager::alloc_at(thread_stack_vm, 4, MMU::NoExecute | MMU::ReadWrite));
|
||||
|
||||
Stack thread_stack{thread_stack_vm, ARCH_PAGE_SIZE * 4};
|
||||
thread->set_sp(thread_stack.top());
|
||||
|
||||
g_threads.append(thread);
|
||||
|
||||
kinfoln("CREATED THREAD: id %lu with ip %lx and sp %lx", thread->id, thread->ip(), thread->sp());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> new_kernel_thread(u64 address)
|
||||
{
|
||||
Thread* thread = TRY(new_thread());
|
||||
thread->init_regs_kernel();
|
||||
thread->set_ip(address);
|
||||
|
||||
return kernel_thread_alloc_stack_and_append_impl(thread);
|
||||
}
|
||||
|
||||
Result<void> new_kernel_thread(void (*func)(void))
|
||||
{
|
||||
Thread* thread = TRY(new_thread());
|
||||
thread->init_regs_kernel();
|
||||
thread->set_ip((u64)func);
|
||||
|
||||
return kernel_thread_alloc_stack_and_append_impl(thread);
|
||||
}
|
||||
|
||||
Result<void> new_kernel_thread(void (*func)(void*), void* arg)
|
||||
{
|
||||
Thread* thread = TRY(new_thread());
|
||||
thread->init_regs_kernel();
|
||||
thread->set_ip((u64)func);
|
||||
thread->set_arguments((u64)arg, 0, 0, 0);
|
||||
|
||||
return kernel_thread_alloc_stack_and_append_impl(thread);
|
||||
}
|
||||
|
||||
Thread* pick_task()
|
||||
{
|
||||
Thread* old = g_current;
|
||||
if (old->is_idle())
|
||||
{
|
||||
auto maybe_last = g_threads.last();
|
||||
if (maybe_last.has_error()) // No threads!!
|
||||
return &g_idle;
|
||||
g_current = old = maybe_last.value();
|
||||
}
|
||||
|
||||
bool has_found_thread = false;
|
||||
|
||||
do {
|
||||
auto maybe_next = g_threads.next(g_current);
|
||||
if (maybe_next.has_error()) g_current = g_threads.expect_first();
|
||||
else
|
||||
g_current = maybe_next.value();
|
||||
|
||||
if (g_current->state == ThreadState::Runnable)
|
||||
{
|
||||
has_found_thread = true;
|
||||
break;
|
||||
}
|
||||
} while (g_current != old);
|
||||
|
||||
if (!has_found_thread) g_current = &g_idle;
|
||||
|
||||
return g_current;
|
||||
}
|
||||
|
||||
void generic_switch_context(Thread* old_thread, Thread* new_thread, Registers* regs)
|
||||
{
|
||||
if (old_thread != new_thread) switch_context(old_thread, new_thread, regs);
|
||||
|
||||
if (new_thread->is_idle())
|
||||
{
|
||||
new_thread->ticks_left = 1; // The idle task only runs for 1 tick so we can check for new runnable tasks
|
||||
// as fast as possible.
|
||||
}
|
||||
else
|
||||
new_thread->ticks_left = TICKS_PER_TIMESLICE;
|
||||
}
|
||||
|
||||
void switch_task(Registers* regs)
|
||||
{
|
||||
Thread* old_thread = g_current;
|
||||
Thread* new_thread = pick_task();
|
||||
generic_switch_context(old_thread, new_thread, regs);
|
||||
}
|
||||
|
||||
void invoke(Registers* regs)
|
||||
{
|
||||
CPU::disable_interrupts();
|
||||
|
||||
g_current->ticks++;
|
||||
|
||||
if (is_in_kernel(regs)) g_current->ticks_in_kernel++;
|
||||
else
|
||||
g_current->ticks_in_user++;
|
||||
|
||||
g_current->ticks_left--;
|
||||
|
||||
g_threads.for_each([](Thread* thread) {
|
||||
if (thread->state == ThreadState::Sleeping)
|
||||
{
|
||||
if (--thread->sleep_ticks_left == 0) thread->state = ThreadState::Runnable;
|
||||
}
|
||||
});
|
||||
|
||||
if (!g_current->ticks_left) switch_task(regs);
|
||||
}
|
||||
}
|
||||
|
||||
void kernel_sleep(u64 ms)
|
||||
{
|
||||
g_current->sleep_ticks_left = ms;
|
||||
g_current->state = ThreadState::Sleeping;
|
||||
kernel_yield();
|
||||
}
|
23
kernel/src/thread/Scheduler.h
Normal file
23
kernel/src/thread/Scheduler.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "thread/Thread.h"
|
||||
|
||||
namespace Scheduler
|
||||
{
|
||||
void init();
|
||||
|
||||
Thread* current();
|
||||
Thread* idle();
|
||||
|
||||
Result<void> new_kernel_thread(u64 address);
|
||||
Result<void> new_kernel_thread(void (*func)(void));
|
||||
Result<void> new_kernel_thread(void (*func)(void*), void* arg);
|
||||
|
||||
Thread* pick_task();
|
||||
|
||||
void switch_task(Registers* regs);
|
||||
|
||||
void invoke(Registers* regs);
|
||||
}
|
||||
|
||||
extern "C" void kernel_yield();
|
||||
void kernel_sleep(u64 ms);
|
15
kernel/src/thread/Thread.cpp
Normal file
15
kernel/src/thread/Thread.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "thread/Thread.h"
|
||||
#include <luna/Alloc.h>
|
||||
|
||||
static u64 g_next_id = 1;
|
||||
|
||||
DoublyLinkedList<Thread> g_threads;
|
||||
|
||||
Result<Thread*> new_thread()
|
||||
{
|
||||
Thread* thread = TRY(make<Thread>());
|
||||
|
||||
thread->id = g_next_id++;
|
||||
|
||||
return thread;
|
||||
}
|
59
kernel/src/thread/Thread.h
Normal file
59
kernel/src/thread/Thread.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "arch/CPU.h"
|
||||
#include <luna/LinkedList.h>
|
||||
#include <luna/Result.h>
|
||||
|
||||
#ifdef ARCH_X86_64
|
||||
#include "arch/x86_64/CPU.h"
|
||||
#else
|
||||
#error "Unknown architecture."
|
||||
#endif
|
||||
|
||||
enum class ThreadState
|
||||
{
|
||||
Idle,
|
||||
Runnable,
|
||||
Sleeping
|
||||
};
|
||||
|
||||
struct Thread : public DoublyLinkedListNode<Thread>
|
||||
{
|
||||
Registers regs;
|
||||
|
||||
u64 id;
|
||||
|
||||
u64 ticks = 0;
|
||||
u64 ticks_in_user = 0;
|
||||
u64 ticks_in_kernel = 0;
|
||||
|
||||
u64 ticks_left;
|
||||
u64 sleep_ticks_left;
|
||||
|
||||
ThreadState state = ThreadState::Runnable;
|
||||
|
||||
bool is_idle()
|
||||
{
|
||||
return state == ThreadState::Idle;
|
||||
}
|
||||
|
||||
void init_regs_kernel();
|
||||
void init_regs_user();
|
||||
|
||||
void set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4);
|
||||
|
||||
void set_ip(u64 ip);
|
||||
u64 ip();
|
||||
|
||||
void set_sp(u64 sp);
|
||||
u64 sp();
|
||||
};
|
||||
|
||||
void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs);
|
||||
|
||||
bool is_in_kernel(Registers* regs);
|
||||
|
||||
Result<Thread*> new_thread();
|
||||
Result<Thread*> create_idle_thread();
|
||||
|
||||
extern DoublyLinkedList<Thread> g_threads;
|
@ -2,8 +2,8 @@
|
||||
#include "boot/bootboot.h"
|
||||
#include "video/Framebuffer.h"
|
||||
#include <luna/Format.h>
|
||||
#include <luna/String.h>
|
||||
#include <luna/Result.h>
|
||||
#include <luna/String.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
extern const BOOTBOOT bootboot;
|
||||
|
@ -7,6 +7,7 @@ set(FREESTANDING_SOURCES
|
||||
src/Units.cpp
|
||||
src/SystemError.cpp
|
||||
src/Bitmap.cpp
|
||||
src/Stack.cpp
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@ -41,4 +42,10 @@ target_compile_options(luna-freestanding PRIVATE -mno-red-zone)
|
||||
target_compile_options(luna-freestanding PRIVATE -mno-80387 -mno-mmx -mno-sse -mno-sse2)
|
||||
target_compile_definitions(luna-freestanding PUBLIC ARCH_X86_64)
|
||||
target_compile_definitions(luna PUBLIC ARCH_X86_64)
|
||||
endif()
|
||||
|
||||
if(LUNA_DEBUG_SYMBOLS)
|
||||
message(STATUS "Building Luna with debug symbols")
|
||||
target_compile_options(luna PRIVATE -ggdb)
|
||||
target_compile_options(luna-freestanding PRIVATE -ggdb)
|
||||
endif()
|
@ -106,11 +106,21 @@ template <typename T> class DoublyLinkedList
|
||||
return nonnull_or_error((T*)m_start_node);
|
||||
}
|
||||
|
||||
T* expect_first()
|
||||
{
|
||||
return first().value();
|
||||
}
|
||||
|
||||
Result<T*> last()
|
||||
{
|
||||
return nonnull_or_error((T*)m_end_node);
|
||||
}
|
||||
|
||||
T* expect_last()
|
||||
{
|
||||
return last().value();
|
||||
}
|
||||
|
||||
Result<T*> next(T* item)
|
||||
{
|
||||
return nonnull_or_error((T*)extract_node(item)->get_next());
|
||||
|
23
luna/include/luna/Stack.h
Normal file
23
luna/include/luna/Stack.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <luna/Types.h>
|
||||
|
||||
struct Stack
|
||||
{
|
||||
Stack(u64 base, usize bytes);
|
||||
|
||||
u64 bottom()
|
||||
{
|
||||
return m_base;
|
||||
}
|
||||
|
||||
u64 top();
|
||||
|
||||
usize bytes()
|
||||
{
|
||||
return m_bytes;
|
||||
}
|
||||
|
||||
private:
|
||||
u64 m_base;
|
||||
usize m_bytes;
|
||||
};
|
@ -62,11 +62,14 @@ void Bitmap::clear_region(usize start, usize bits, bool value)
|
||||
expect(initialized(), "Bitmap was never initialized");
|
||||
expect((start + bits) <= size(), "Bitmap clear out of range");
|
||||
|
||||
if (!bits) return;
|
||||
|
||||
// Set individual bits while not on a byte boundary.
|
||||
while ((start % 8) && bits--)
|
||||
while ((start % 8) && bits)
|
||||
{
|
||||
set(start, value);
|
||||
start++;
|
||||
bits--;
|
||||
}
|
||||
|
||||
// Clear out the rest in bytes.
|
||||
|
@ -2,8 +2,8 @@
|
||||
#include <luna/Format.h>
|
||||
#include <luna/NumberParsing.h>
|
||||
#include <luna/Result.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
extern "C" usize strlen(const char*);
|
||||
|
||||
|
10
luna/src/Stack.cpp
Normal file
10
luna/src/Stack.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include <luna/Stack.h>
|
||||
|
||||
Stack::Stack(u64 base, usize bytes) : m_base(base), m_bytes(bytes)
|
||||
{
|
||||
}
|
||||
|
||||
u64 Stack::top()
|
||||
{
|
||||
return (m_base + m_bytes) - sizeof(void*);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
#include <luna/Format.h>
|
||||
#include <luna/Units.h>
|
||||
#include <luna/Result.h>
|
||||
#include <luna/Units.h>
|
||||
|
||||
Result<usize> to_dynamic_unit(usize value, char* buffer, usize max)
|
||||
{
|
||||
|
@ -9,7 +9,7 @@ tools/setup.sh
|
||||
|
||||
tools/full-clean.sh
|
||||
|
||||
cmake -S . -B $BUILD_DIR -DMOON_DEBUG_SYMBOLS=ON -G "$CMAKE_GEN"
|
||||
cmake -S . -B $BUILD_DIR -DMOON_DEBUG_SYMBOLS=ON -DLUNA_DEBUG_SYMBOLS=ON -G "$CMAKE_GEN"
|
||||
cmake --build $BUILD_DIR
|
||||
cmake --install $BUILD_DIR
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user