@twilit smelt quick question, https://github.com/xrarch/mintia2/blob/main/OS/Executive/Ke/xr17032/KeException.jkl#L178 shouldn't checks like these (there are multiple occurences of it) also include whether or not IPL < DPC? Because I don't think software ints should be dispatched at IPL_DPC unless I'm misunderstanding the code, I had to add this check in my kernel and was wondering if you also had come into the same issue that made me add it, so I checked if mintia also did it
#MINTIA (not vibecoded)
1 messages · Page 11 of 1
maybe I'm also missing something else
this is what I was missing
ok that's smart
but then wouldnt that result in a negative shift for ipl=0
Yes
oh yeah right
Software interrupts at ipl 0 aren't a thing
there are no softints at ipl=0
ipl0 cant have soft ints by definition because ipl is unsigned and all interrupts with ipl >= current ipl are masked off
so youd need an ipl below 0 to have any soft ints at ipl0 be delivered
which donut exist
Yeah idk why I didn't think of this immediately
ive been slacking on mintia
ewwww netbsd has a giant mutex around its buffer cache
also i learned what the xnu strategy for avoiding priority inversion with shared locks is, since you cant reasonably inherit priority through shared holders
they just revert to priority ceiling
when you take an rwlock shared, your priority is raised to a maximum
@warm pine
there is much to be said for this
source: cogolein
actually i will admit that i secretly harbour a suspicion that it could be that priority inheritance is a cargo culted feature, at the cost of the complexity in implementing it, and reduced ability to reason about the priority at which some work is carried out (since now there are countless ways it can be dynamically influenced), and that simpler strategies could be superior most of the time
VMS only ever did priority ceiling, still does
when you take a kernel mutex it raises your thread to max priority until you release it
NT was also spec'd to do this and it actually does so in the april 1991 mips build, but by september 1991 theyd replaced it with random boosting
i am in no way confident about this suspicion and on the contrary i think there is a strong chance i could be wrong, but i do think it's a possibility
which simpler strategies? random boosting etc?
I think the issue with these is that they are not very useful for real time applications
priority ceiling mainly
trivial to reason about
random boosting is random so it's out
it's also not a technique i know of being used outwith the very particular case of active deadlock breaking by nt
the lowering part of priority ceiling in april 1991 NT
if the thread's held mutex list is non-empty v2 + 24 != *(_DWORD *)(v2 + 24), return
otherwise it became empty, so the priority is lowered back to a value previously stored in the thread
also notice the elusive KeReleaseMutex
another thing that was gone by september 1991
the mutex object
this is a VMS-ism that disappeared by first release of NT
there are several others
the issue with priority ceiling is that you have to know the ceilings of all locks in advance, don't you?
VMS (and ancient pre-release NT) would just raise to max priority
max user thread priority at least
with the assumption the kernel threads will behave well
yeah
if the thread's base priority was below 16, its raised to 16
which is the largest user priority
16-31 are real time priorities
actually this is the lowest real time priority
not highest user priority
so this thread couldnt even be preempted after taking this mutex
that sounds like it could easily be used to starve the rest of the system
no because kernel codepaths are controlled so you can just Know there arent places where a mutex is held for a veeeery long time without blocking
this only applies to kernel mutexes
i disagree with real time threads not being preemptible though
if they must be nonpreemptible then this really should have been highest user priority rather than lowest real time priority
imo
what's the point of having more than one real time priority if real time tasks are not preemptible (by higher prio tasks)?
it still determines the order the next one will be selected in when the current one blocks
this practically makes huges swathes of the kernel nonpreemptible
clearly they had a sit down and decided the entire idea was bunk at some point between april and september 1991 and just replaced it with random boosting
@warm pine behold: the VMS balance set mechanisms, in NT
im not completely sure what these are doing other than the first is presumably sweeping the ready queues and removing all of the excluded process's ready threads (thereby forcibly suspending them. bad idea, deadlocksville, better to let them run and suspend themselves at a quiescent point e.g. before returning to usermode) and the latter is unblocking them. the former in preparation for a whole-process swapout and the latter upon bringing it back in
i just had a very QUAINT idea
i could literally look at the VMS sources for this and theyll probably look extremely similar but in VAX assembly and commented
relevant code should be in here
already a good file
a question some of the off topic channel dwellers here should be asking themselves
special cases for multithreaded processes
fun fact: they didnt add true multithreading to VMS until after the Alpha port, so this is VAX assembly that never even ran on a VAX
Is this decompiling? Interesting, what __fastcall for mips looked like. I only know o32 😁
yes this is a decompilation
i think i gave him that one
include and exclude in terms of in and out of the system balance set ?
yeah
yeah it doesnt
so i actually experimented with implementing a balance set in old mintia and multithreading really does complicate it
the need to suspend all threads of a process
this isnt actually hooked up to anything in april 91, its implemented but never called
the way you do this is critical
interesting, did it have lpc back then ?
it seems VMS forcibly suspends all threads of the process on the spot
it does this by removing any ready ones from the run queues
and placing them on a list, anchored in the process, of threads that need to be re-readied when the process is brought back into the balance set
and any that are unblocked after the process has been selected, are placed on the same list
so when you call the routine to remove a process from the balance set, it returns with the process Really Removed
the way it avoids certain deadlocks is to special case the holding of various mutexes in the kernel
where if any threads of a process are holding one of those mutexes, the process is ineligible for removal
NT doesnt seem to have any checks like that implemented in this "stub" implementation of KeExcludeProcess
and its possible it was a real pain in the neck and they decided it was the wrong way to go entirely
the problem with the original inheritance-based PIA is that it works assuming first that resource dependencies are known, and second that we are working with static priorities
also if you have each thread suspend itself when it gets scheduled back in and is about to return to usermode, it could take a loooooooong time for that to actually happen and that defeats the purpose of process swap-out which is an attempt to rapidly obtain lots of memory
i think this was the main issue with the way i tried to do it that made it suckish and not work well
i have a thing now called "lazy APCs" which get dispatched on return to usermode and also whenever the thread does a wait on behalf of usermode (signified by calling the wait function with mode=usermode)
if a wait is on behalf of usermode that signifies a few things like that nothing depends on anything on the thread's kernel stack, so it can be swapped out, and also that its not holding any important locks
so this would be a safe point to do the suspension
i could also temporarily boost their priorities really high to make sure they suspend themselves quickly
once the last one suspends itself it kicks off the remainder of the process of immediately yeeting the process out of memory
NT actually does do whole process swapout, but it only does it opportunistically when all threads of the process have been in a wait-on-behalf-of-usermode for longer than X seconds or so
it doesnt do anything forcible like this
i already use these for thread suspension
i have three kinds
lazy apcs is your invention?
to my knowledge yeah
they replace the function of "normal APCs" in NT
which are much scarier and more difficult to work with
"special kernel APCs" in NT are identical to my "kernel APC"
"normal kernel APCs" dont exist for me and are subsumed by the "lazy APC" which is only dispatched at the times noted in the comment
normal kernel APCs basically are dispatched whenever an "APC disable count" is zero inside the thread, and IRQL < APC_LEVEL
normal kernel APCs can take more locks than special kernel APCs and can initiate IO and stuff
so filesystem drivers have to explicitly increment the disable count upon entry and decrement upon exit
which imo is very fragile and fraught with terror
using KE_USER_MODE waits as a canonical quiescent point within the kernel is much easier to keep track of
i think this would be basically perfect for use to implement the balance set exclusion suspension thing
this is an example of that count being incremented in a filesystem driver, the FsRtlEnterFileSystem() macro literally just increments the normal apc disable count for the current thread
they have to remember to do this and undo it perfectly in like dozens of places
if not hundreds
is this basically a synonym for KeEnterCriticalRegion ?
lazy apcs are probably dispatched a bit less promptly cuz this quiescent point comes by less frequently than the disable count being 0 in the NT kernel, but are infinitely more useful and less invasive into the implementation of the rest of the kernel
imo at least
i guess
btw i'm too dumb to figure out, what's PIA?
priority inversion avoidance probably
private internet access
that's what I meant
also any potentially indefinite or long-lived user-caused wait needs to be a KE_USER_MODE wait or this scheme is broken
it only works if the time between such waits is bounded and ideally fairly short
and any unbounded wait can be interrupted by LAPC delivery
so for example any wait on like file IO needs to be that
@twilit smelt i heard u were good at evaluating kernel quality
what do u think of this https://forum.osdev.org/viewtopic.php?t=57825
wondering if people might be willing to tip me regular monthly funding to finish it up
and
Demand paged
they claim to have memory and cpu hotplug but that seems to be bullshit
idk about the quality but the filenames are annoying me
also only 32-bit x86 major L
Yeah
Look at the linker file
It looks insane
And why is there a Trib suffix everywhere
Have you looked at how its actually used?
wheres the linker script tho
From a quick glance its kinda not
I cant find it
Uhh lemme see
how does memory hotplug work?
Tldr aml notification
?
Oh u don't know what aml is?
ik what it is, but like, you need to keep a backup of that memory, no?
Hm?
surprise unplug obviosuly doesn't workl
ah XD
U get notified of a request to unplug
isnt there also CPU hotplug that's possible
yes
If you successfully do it then you tell the firmware it can be removed
Yeah and unplug which this kernel claims to have
cool
i might look at implementing that
but idk how to even test it
Its very simple
Hmp commands in qemu
oh
Device_add ...
and to remove?
Its simple if u have aml support, which this kernel doesnt and the rsdp parsing i see there is crappy also
It will wait of course
i have uacpi, so i just need to implement listeners for it?
Yep
how do you even unplug ram, do you just remove it from the socket
GitHub
Official QEMU mirror. Please see https://www.qemu.org/contribute/ for how to submit changes to QEMU. Pull Requests are ignored. Please only use release tarballs from the QEMU website. - qemu/qemu
15 year old project 😲
exactly 15 year old project lol
as of today
I mean yeah
but how will it "wait" when you can just yeet the stick out
If u do that it just crashes
it will tell you when to remove them i suppose
ahh
that's a very cool feature to have
lol
At least images I've tested
are those features only on server boards?
wait so I'm guessing it's somehow possible to figure out which sticks of RAM have which address ranges?
kewl
well, the firmware surely knows
Yes, aml tells u the ranges for hotpluggable sticks
Yes
SRAT, HMAT, and PXM?
i forgot which ones i thinks
Those are acpi tables
strange that windows doesn't let you hotplug cpus and memory
Yeah
they want u to buy more ram and never get rid of it
you could restart the server to unplug
isnt there some strange limitation on RAM for windows home that isnt present on windows server and stuff on many versions
yes
64 for home and 256 for pro i think
no, i think they still limit you anyway
no i mean windows 10 home doesnt let u have more than 128GB RAM
64-bit
Its kinda hard to get more on a consumer board
🙁
But yeah
marketing and money probably, idk i dont see why there would be a physical limitation or whatnot
no it's likely a software reason
Yeah
I doubt its a marketing thing
updated to 2TB for pro and 6TB for pro workstation or enterprise, home is still limited to 128
I doubt they're so bad at programming they won't support more than 512 gb of ram lol
these are the latest values
6TB max
they allow 24TB for servers
Maybe just different configs for different targets
Less max phys bit less wasted metadata or something idk
nah, most likely marketing
windows, the famously space efficient os 💥
firefox on windows: hmm, lets eat 50Gb of ram because of a mem leak...
(yes, this happened)
and not just 1 time
I stopped using ff because of this fkn memory leak years ago. And it's still there. what a wonderful piece of s... software.
Use zen
It's fixed there
Also, when it reaches max mem, it will reset back to normal usage
Seems very messy
Difficult to tell what it does at a glance
I'll have to look harder later
With my glance I didn't see any locking
I'm sure it's there but the implication is that it's very coarse
big scheduler lock or some such
Only lock implementation I can find is a spinlock
Which they refer to as "wait lock"
Bad sign
But I might just not be spotting their beautiful turnstile backed mutex or whatever yet
First impression is that it's way higher effort than most hobby kernels but there's a few tell tale signs that the person isn't very well read
which has limited their ability to make this good
the usage of UDI makes me think their reading was mostly the osdev wiki cuz UDI is a huge boomerism (fadanoid has talked about it before)
The numa support seems to be "I can drive the hardware minutia related to numa" rather than like high scalability
It doesn't exist outside of vms
why is the main code in a folder that starts with two underscores 😭
like
id expect two underscores to be something unimportant
Also, NUMA support is not a very exciting feature in reality
NUMA support mostly consists of splitting your physical memory allocator into multiple nodes
its one of those things that has a fractal complexity the "better" you want to be at it
The actual NUMA balancing algorithms are usually quite boring
this is like step #1 but some people have gone much much further than this
Usually it's either "prefer near memory" or striping (for programs that operate on the same memory range with many cpus)
for shared memory parallel programs (say openmp) with a good access pattern, it's often very hard to beat striped numa memory
and for simple / short lived applications, just allocating from a near node is good enough
That is because this configuration truly allows you to pull from multiple memory controllers in parallel on the hw level
I saw a really cool linux RFC that did kernel code duplication for each numa so each cpu would run a copy from their own numa
it had that for userspace as well, but they found it was only useful for super long running programs
that was my thoughts as well
and if you dont have numa balancing code you will eventually have super fragmented numa balance and have shit allocate from random nodes that have memory left
ANCIENT technique
this was done in cellular irix in the 90s and is described in a book about openvms from like 2003
the VIRBND thing is interesting
i guess the later alpha chips had two separate page table roots and the one looked up on a tlb miss depended on whether the VA was above or below the value of that register
they did software tlb miss handling but it was still architecturally done "for you" because the tlb miss handler was in firmware
For userspace it seems quite complicated to implement on most archs
well its only for .text so i guess not that bad
As you need to ensure that it's transparent
yeah
in particular, that concurrent cpus cannot see intermediate states even when mapping one code page over another code page
Alternatively you could make these cases illegal but that's probably not an option for linux
how do u map one code page over another
anyway linux already has core mechanisms to transparently remotely replace page tables of a process so i guess that could just be reused
like with live data
for memory compaction
what did u mean by this btw
and not one copy of the page table for each cpu
it depends on whether you need the mechanism to be fully transparent or not
if you want it to be fully transparent, you need to make sure that stuff like this doesn't happen:
- cpu 1 reads a word W of address P and sends it to cpu 2
- cpu 2 reads address P and compares it to W
- cpu 3 remaps address P to another page
- cpu 2 sees the old contents at P while W is equal to the new contents of P
well if cpu3 is still in the mmap syscall cpu2 is racing against it anyway
like isnt this the exact same problem as munmap
well, without per-CPU page tables there are way fewer failure conditions here
like I guess u have to "tlb invalidate" when mapping as well
so that other cpus refresh their page table as well
consider the case that P is not initially mapped, cpu 1 successfully reads P but cpu 2 faults
this cannot happen without duplication of pts per NUMA domain
ngl i dont understand how this isnt a bug in the userspace program
well it all depends on what guarantees your memory mapping primitive provides
like if you dont finish the syscall its not done
you cant expect for its result to be visible
but certainly a naive implementation of this duplication feature is detectable by user space code without kernel interaction
yeah i guess
u could have the faulting cpu check a bit and retry the fault
to easily work around this
this is probably not an issue for your average userspace program that'll never do something insane like this
i wish i could do that when i woke up
page fault retry my beloved
a good bandaid for 5000 different problems
but it might be for example for virtual machines that rely on race free behavior for correctness
linux already does page fault retries if you blow on the handler wrong
everyone does
or tracers and debuggers inserting breakpoints etc
its the only reasonable way to work around some races
bsds, xnu, nt, linux all do it
perhaps, but yeah id just make it retry the fault
if you dont have at least one page fault retry path its probably because your vmm is trivial
fair
you need to handle spurious faults even without duplicated pts
my point is that it gets more complicated with duplicated pts
not that it's impossible
i mean yyeah lol of course
and it requires more heavy synchronization
i'm pretty sure that i can construct a case on x86 that detects this unless the kernel does something like break before make
thats why they found out it was worse for short lived programs
the fancy ones use like "did the world change?" sequence numbers to try to avoid it when possible but even those ones just give up, back out, retry sometimes. the page fault handlers in all modern kernels are structured as an outer loop around the page fault logic
which retries forever until theres a result other than "there was a weird race condition it would take another 4000 lines of code to work around so just run me again instead"
yeah you basically need to check if the page is mapped on some archs anyway
i'm pretty sure that there are archs that only guarantee eventual consistency for the tlb (rv might be one but i'd have to check the docs to be sure)
= you can't be sure that no pf will be triggered even if the page is mapped in the pt
of course high quality implementations will usually avoid this but i'm not sure if it's architecturally guaranteed
yeah (you probably know this but just for expository purposes) there are also architectures that cache invalid PTEs in the TLB which can cause spurious page faults like MIPS
yeah true
and your page fault handler sees the pte's valid bit is set and goes "oh" and flushes the page from the tlb and returns
this was common in software refill architectures because it removes a branch from the tlb refill handler
namely the branch to check the pte's valid bit and branch off to the page fault handler if its clear
cpu does it for you instead so you can just blindly load the pte into the tlb without caring about whether the valid bit is set or not and return
unsure if loongarch does this
it's not very exciting if you're not
if you are then you can do something outrageous like cellular irix
jinx
you can treat NUMA as "let's try and make an SMP kernel run better on it" or as "let's try and make a kernel that looks more like one for running on distinct nodes of a distributed cluster, and carefully introduce mechanisms for explicit sharing/loaning/migration of resources"
as for zambezi i've been watching it for some time
and last i looked i thought it looked as though there were at least some nice ideas but too young to judge yet and far too eccentric with its naming
if you think it's hard to keep track of what's what in Managarm when everything has an arbitrary name like lewis, michael, iain, or gordon, then wait until you see zambezi, it's even worse
i hadnt seen it until just now
it got no blocking mutexes that i could find
so much for scaling
i think they prefer using, i'm not sure how to term it, but having more primitive facilities available for blocking on things
https://github.com/latentPrion/zambesii/blob/543c28daa7bf4ae9579f3644d64f1b583adbe607/__kcore/include/kernel/common/lock.h#L45 this thing is used seeming to that effect in some places
https://github.com/latentPrion/zambesii/blob/543c28daa7bf4ae9579f3644d64f1b583adbe607/__kcore/kernel/common/taskTrib/taskTrib.cpp#L236
well thats just passing in some kind of descriptor structure that describes in what way a spinlock is currently held
and it gets unlocked after doing some stuff to make sure there arent missed wakeups
right?
xv6 has a similar thing, you can pass the address of a spinlock into the context switch function to get it to do the same, for the same reason
oh really
also is a thing in lots of unixes, i noticed illumos has the same and its used in the turnstile impl
NT solves the same problem by having a sticky event flag bit
only after the move towards good SMP support (mostly by copying what solaris did) did most of the BSDs start moving towards unifying on their sleeping locks
prior to that they used a primitive like this which was sufficient to implement various blocking locks of subtly differing behaviours
someone should tell them that c++ has static_assert
in illumos the thread structure has a field t_lockp which is a pointer to a spinlock to atomically release after fully switching away from the thread
the turnstile chain lock is released this way when a thread blocks on one
something really funny is that if i look up "osdev" in any random coding discord
a ton of the results are people going like "osdev discord traumatized my friend :/"
all the people who ragequit here go out into the wild and talk about how this discord is traumatic and warning people away from it
snowflakes
picked a random one and found this
yes its very hard to explain to a beginner that a proper bootloader that actually works is a multiyear project
i love the popular interpretation of the limine shilling as being like
"osdev discord HATES anybody who tries to do anything on their own! theyre jealous of our High Agency!"
meanwhile my project is the NIH of NIH and i dont get any shit for it so obviously thats not it buddy
Imho the right messaging for this is telling people that if you want to write a kernel, it is a terrible first step to write a bootloader
As a good bootloader looks nothing like a good kernel
yeah
like literally no one cares if they want to make a shitty boot sector that reads a hardcoded number of sectors to load their kernel
we just know how those usually end
and what bugs it leads to
it's just boring and tbh if it seems alluring then one has to ask what interest you have in writing an actual kernel
yep
on the other hand there's also the appeal of controlling everything from boot up
the usual idea is "well since im writing a kernel surely i must write a bootloader lol"
plus, a beginner's goal is probably not to write a great kernel anyway
in many cases
then you're missing controlling everything from firmware up
yeah but dealing with bios quirks and real mode crap is not exactly useful for kernel dev
so u gotta figure out what interests u
but if you just flash your bios then you're missing something else, viz. assembling your own computer
but if you put together a single-board computer with, say, an off-the-sheld motorola 68040, you're missing out on the experience of assembling your own integrated circuits
NIH always has a cutoff point, for many people that's "to change this you have to flash firmware"
for other people that's "this will not be used after boot" (i.e. the bootloader can still be third party)
we all pick a cutoff point yeah
u can always just decide to make a working kernel first and them implement a bootloader later if you're still interested in that
to at least eliminate these sorts of bugs
re the NUMA discussion: it is true that there are more optimizations that you can design beyond the basic strategies but the basic strategies get you to 90% or even 95% of the optimum. like with scheduling, the important part about NUMA is not trying to do something great but making sure that you don't do anything exceedingly stupid.
ppl dont get mad over where you put your cutoff point they usually get mad about how badly youre probably going to do it with where you are now
so people get annoyed when beginner #23932 wants to do his own bootloader because its gonna suck and be broken and teach him almost nothing but dont get annoyed when i have my project where i nih'd a lot more than just the bootloader because it already worked before i showed it off
I guess instead of saying that NUMA is not exciting the better phrasing is that getting good (but not perfect) NUMA performance is surprisingly simple
most of the times i get annoyed at people writing their own bootloader it's because the kind of help they're asking for makes it clear that they're wayy in over their heads
like the combination "this is my first os dev related project + I am writing my own bootloader + I am unwilling to do research beyond the first 10 words of the first Google result" is very common and very annoying
yeah you can get away with anything here if youre on a footing of "i did a good chunk of the thing and then talked about it" rather than "im desperately begging for help on how to do the thing"
if youre the latter and youre talking about doing something unusual its definitely out of stupidity rather than a well-calculated experiment
I think this is mostly because all the "writing my own os!!" tutorials start with shitty bootloader that reads a fixed number of sectors from a fixed starting sector and assumes the sector size is 512 and uses CHS functions, which is not viable once you start doing anything even remotely serious
i dont relate to people who learned from those tutorials because i feel like i came at it from a completely different angle where i wanted to do my own computer and there were no tutorials for that so i learned how from papers about like risc workstations and their operating systems and architecture manuals
and i feel like more people should do that lol
way better at honing research skills and engineering sense and stuff than most other routes into osdev i think
definitely yeah
reading papers and catching up through the decades really lets you know why things are the way they are and what factors are super important and so on
because they discuss motivation
from an industry/academia insider perspective
which is wayyyy more valuable than whatever discussions are on the osdev forums or wiki or tutorials
certainly doesn't help that all the osdev tutorials are made by people who themselves don't really know what they're doing (the people who do are too busy working on their os to make tutorials)
@warm pine random thing: VMS doesnt eagerly reap dead threads, it lets their corpses pile up until memory is low and then it frees them all at once (the swapper does this just whenever its awoken, for any reason. it is never explicitly awoken to free dead threads, so this tends to happen when memory is low)
interesting little difference from the norm
seems highly intentional too like they decided that was superior to having a worker thread wake up and do it all the time
ive considered doing something like a tutorial but all my personal preferences would inevitably be encoded and everybody would probably hate it for spawning dozens of mini mintias
the hypothetical korona tutorial would probably spawn 30 mini managarms
both better than what we have now
odd, i wonder why
it's not as though they're doing something like ad hoc slab allocation where the dead threads can be recycled into new ones?
nope
i say that present tense but that was only true as of 2003
could have changed in the 22 years of active development since then
reaping dead threads is considered a memory-making activity
and therefore the obligation of the swapper
i could see it being better if for some reason youre creating and destroying threads at a very high frequency
eliminating the excess preemption from a kernel worker thread freeing them constantly and doing it all at once at intervals could be a lot faster
so like @mortal thunder 's fireworks test might be accelerated by this lol
better mini mintias than 10000 kernel shells but yeah
10000 paging.asms
RE: osdev traumatised blah blah
is it that pervasive? because tbh we're not that mean to anyone? like, other than telling people "hey, this stupid thing you're doing is stupid", that is
i do regularly see people bitching about osdev discord in random places
no one here has ever prevented or bitched to anyone e.g. writing their own bootloader if they know what they are doing
but rest assured its almost always whiny crybabies who dont matter
idk what their idea of what we're supposed to do is? like, use tons of emojis, saying that vibe coding is the future, and encourage them to do anything they want, despite the fact that it will very likely end up in failure and waste of time?
lol
yeah
Ever since it got removed
One thing I see more of again is cfenollosa
The wiki
this just made me decide to set a 1 second timer in the future for thread reaping when a thread is placed on the reaper list @warm pine i think this is probably a decent idea (sorry for second ping)
did it originate from there?
i honestly don't even remember where it came from
i thought it was brokenthorn
and then like
spread like some sort of weird mutation gene
god no
i'd hate myself if that was the end result
i don't think i am a good kernel designer, i just do what's fun, and most of the time that doesn't align with what's best
plus
i think kernel design fundamentally cannot be encoded in a tutorial
i dont think anybody is a good kernel designer
i think some industry teams are collectively good kernel designers
but every individual sucks at it
yeah possibly
too many concerns to juggle
you can spend a lifetime becoming an expert at any one of those concerns
and still miss some shit
our projects are all doomed to have a bunch of stuff that STINKS
people tend to misinterpret "what you are doing is stupid" as "you are stupid"
well lets not lie to ourselves we also sometimes just say "you are stupid"
collective "we"
yeah for sure but they take it so personally
it probably orginated from brokenthorn, but made it on to the wiki
but most got it from the wiki
thread reaping is batched now, 500ms worth of terminated threads will be reaped at once
when mm is more fleshed out itll trigger reaping early during low memory as well
I usually do not do that
unless I sniff bad faith, then maybe I'll accuse them of lying
.
might batch deferred object deletion too
since thats another thing that could cause worker threads to be scheduled at an unduly high frequency
so ill probably want to generalize this to a "timed work item"
what does bad faith smell like
From where does your workqueue system comes from?
The balanced queue stuff
which system did i steal the idea from?
Yea
NT
I was looking at NT but couldn't find that much details in win internals at least
Yeah
i called it a balanced queue to contrast with the request queue which is a thing i have that NT doesnt
But apart from a small paragraph there's nothing on it
and is my analogue to NT's Ke-level "device object"
They talk briefly about ExQueueWorkItem
like that reverse instruction set thing, for example
the what
or excessive self-advertising
also i want to amend this and mention that i did actually get a lot of shit for my project in like 2020
here
you're in for a treat
but the regulars from then are gone and the new ones are cooler so i havent since i came back in late 2021
are there not regulars from 2020 still around?
the problematic ones arent
other than justified shit, which i do still get
but im talking more along the lines of belittling the premise and telling me im wasting my time and shit
people would do way back then
did that include Carver?
no carver was cool back then
i understand he has some crazy politics but that never came up at the time lol (at least not where i could see it)
i dont wanna name names in case someone returns and name searches and it causes mysterious beef down the line
example of justified shit is @cunning mortar telling me dragonfruit was a sunk cost fallacy which successfully shamed me into doing a new language thats way better
and was long overdue
do i actually want to read through this or is my brain gonna melt out of my ears
you'll only know after you read it
Jackal is so much better
mind you the compiler is shit but like i can just write a new one and slide that in at any time, which is infinitely better than the entire language being shit
also someone's doing a new jackal compiler already
sandwichman
he recently reached the milestone of the entire parser of the old jackal compiler (5k lines) parsing and type checking correctly in his compiler's frontend
very cool stuff
A C backend would be really cool
for which compiler
current jackl compiler already has a c backend (which is how it builds itself for real computer)
Ah so you could theoretically already port to amd64?
Oh wait I think czapek did exactly that lol
someone already got mintia kernel up to the crash screen by doing that yeah lol
yep
it was fun
my favorite newsdk thing is how theres like 4k lines of generic frontend code copypasted three times between the compiler, assembler, and build system with subtle changes in each
kernel guy tries to write userspace tooling:
yeah that made me puke when i tried to do an amd64 backend
i wanted to separate it out but when i tried it was so dense with ifdefs it almost felt better to leave it repeated
its something i need to give some attention to though lmfao
i do feel guilty about it. i have self awareness.
u know how they would do new unix ports in the 80s though right
for example when it was ported to mips
they created a new directory called mips
copypasted the entire kernel tree into it
oh no.
from one of the other ports
and then changed things willy nilly in there
until it ran on mips
that's painful
so each time it was ported it was basically forked off into a new kernel that evolved independently from the others
and theyd rarely sync up except with great effort
i hope it didn't take them that long to figure out they can just put all those directories in one kernel and swap out the include path
it was done like this primarily because each new port was done by a new company
out of a forked source tree
and they didnt have like github to all coordinate with lol
yeah that is fair
i realized something that made me feel really stupid
which is how to properly add page tables to system space (on 32 bit)
people kept saying "oh you just update the process's page directory to add the new page table when it faults on something in the new part of system space" but this didnt sit right with me because there are moments you could fault on it that would just kill you dead
like architecturally
i could do something like this as well, but honestly, this is not the right moment
right now I just wanna make sure nothing leaks any memory
is there not some architect in the aforementioned industry teams who decides the main design aspects of the kernel ?
i imagine theres a head honcho in the structure but in practice that person isnt an expert in everything and will know that
@acoustic sparrow you often have interesting insights
when a thread dies i now have it enqueue a timer for 500ms in the future, this timer is not re-enqueued for subsequent dead threads
when it expires, all dead threads are reaped in a worker thread
the effect is that i batch reaping in 500ms intervals rather than having a worker thread wake up every time a thread dies
ill also reap it ahead of time when memory is low
do you think this is worthwhile and do u think it could have any surprising drawbacks
that seems potentially really problematic
why
the status code is available in the parent immediately, right?
yeah
this is just the final freeing of the kernel stack basically
which you cant do in the thread's context so you need a worker thread to do it
or i do
so why not reap in the parent?
thats essentially the same thing
as using a worker thread
just with more weird cases
except you dont have a 500ms timer
theres the same issue though of excess preemption if theres a time where threads are being terminated at a high frequency
one other alternative is to have a per-cpu stack for freeing other stacks
i need a full thread context because i need to take blocking mutexes in order to free the kernel stack
all my memory manager locks are blocking mutexes now
yea, you can take a blocking mutex over the freeing stack, then switch to it, then use it as if it waa a normal kernel stack
you might say "theres some other problem if threads are being terminated at a high frequency" which is true but i dont control what the user is doing and id like to be robust against the user doing stupid shit
theres also the problem of the thread object itself which is needed until the very last moment the thread switches away for the last time
because you have stuff like the timer interrupt doing runtime accounting in it and so on
so i also free that at the same time i free the kernel stack
yeah thats a little harder
really i remove a refcount from it which is biased to represent an executing context while its running
so if a process has a handle to this thread object, it still wont be deleted when the reaper gets to it, and the process can examine its exit status and accounting info and so on until it closes the handle
kernel stack is immediately deleted by the reaper tho
but the thread object itself it just unbiases the refcount
which may or may not delete it on the spot
oops
its not
i put that in the deletion routine for the thread object instead
thats not where that should be
i can just move that to the reaper rq
i have a mildly cursed solution to the stack freeing problem
RaiseIRQL();
while (true) {
if (g_prevStack) {
LowerIRQL();
free(atomic_swap(&g_prevStack, null));
RaiseIRQL();
} else break;
}
g_prevStack = current_stack;
pretend you do the atomic swap before lowering irql
and its percpu
I don't get it where this loop go
in the thread exit codep
while you are on the thread stack
Oh I see
Yes I get it
So you just always have a kernel stack sitting around lol
Goofy
its not expensive
That's not cursed at all though it's smart
Yeah even on my 2-4mb target that's nothing
plus, when you need a new one, you can use a defer-freed one
so its really just an alloc cache
this is racey though unfortunately
is it?
if another thread is exiting on another cpu it could free your stack
before you switched off it
assume its a percpu
right you did say that
you have to make sure to keep preemption disabled between setting your stack as the prev stack and switching off it though
they cant free your stack, because you are with a raised irql
you can do it as the very last thing you do
freeing the thread object requires some other thread
well this is interesting to keep in mind, the thread object thing necessitates another thread anyway though
yeah
so i may as well just reap the stack there too
ugly
if there is at least one other ref then you can just unref and its fine, you aren"t freeing
im not ugly your ugly!
otherwise, you can transfer ownership to a worker thread
which is rare anyway
because almost all processes have someone who wants to get the status
unix solves this by offloading it to pid 1
ik the unix solution
yea
i think it makes less sense when everything is a refcounted object
yea maybe
and the lifetime of a thread is determined by you having its handle rather than you not having wait()ed it yet
er process
or whatever
but i think the ownership transfer approach is pretty reasonable
plus, you can also do the same deferred free trick with it
but with a short timer to make sure that threads always get freed at some point
i implemented it badly in aisix in like 2020
also mintia has no process tree
tbh i would have one
the posix subsystem will keep track of one but native mintia has no use for one
if only for introspection
for introspection i keep the parent process's pid as a hint
in the child
that can be invalidated later but hopefully not too quickly
not a weak ref?
nop
but your pids can roll over?
well like i said
yea its not common
there are ways to tell also
that it was invalidated
namely if the "parent" was created later than the child
then you know its not the real parent
oh yea
this is what like task manager does in windows to draw the process tree
the timestamp of creation is stashed in the process object and can be queried
and is used for this
among other things probably
assuming your timestamp doesnt roll over
well mine are 64 bit count of milliseconds
EWWW MILLISECONDS CRINGE WHY NOT NANOSECONDS
shut up
anyway they wont roll over for billion year
i was going to suggest picoseconds 
i mean i could change it to a smaller unit
but yea
you could also just make pids 64 bit
which gets rid of rollover as a concern entirely
i dont do pids as an incrementing count
ah i see
i do it as a free list threaded through the entries of a resizable array indexed by pid
ahh
theres a sequence number on each entry which forms the low like 12 bits of the pid
so that reused table entries dont cycle over too quickly
ah i see
i reuse the exact same structure i use for the per-process handle tables
threads also have ids and go in the same table
one posix thing i allowed to encroach directly into Ps is PGIDs
theres a process group object which implements posix group and sesssion semantics
it even does the thing where a PGID cant be reused as a PID (which would accidentally or maliciously make a new process a retroactive group/session leader) until all of the group members die
when a group leader dies, it atomically exchanges its pointer in the pid table with a pointer to the group object
rather than freeing the entry
the group object removes itself from the pid table when its refcount drops to zero (there are no processes with its pgid anymore)
this was just because like
to me its obvious that its not that difficult to implement the group/session semantics in terms of objects, so its fairly natural
it also would have added an additional layer of fake posix pids in the posix subsystem that just doesnt need to be there
so this bit of contamination seemed fine
im not obsessed with "purity"
ill do whatevers necessary to get posix implemented as quickly and painlessly as possible, ill isolate whats easy to isolate in a Psx- component of the kernel but whats not, i wont worry too much about
the logic of maintaining a process tree will go in Psx but ill allow the structures for it to just be part of the process object, inline; maybe isolated in a PsxProcess struct
posix subsystem will also be kernel mode
which is obvious from what i already said
i assume you're going to end up implementing threads as object manager objects, so that userspace can have handles to them and things like that
so why not use the object manager machinery to destroy the thread stack
are you afraid of unresponsive processes not actually closing the exited thread handle?
"End up" it's already done
Because I can't
why not?
The process can close the handle before the thread terminates
yes
So that's why
in my OS, the thread holds a reference to itself while it's running
Then the only reference to the thread object is the one implied by the fact the thread is running
Yes
Now where do you remove this reference?
i remove it in a DPC that executes after a thread switch, which slams the thread into a queue of objects to remove and wakes up the object reaper thread
my point is that you could have only one such queue instead of two
Okay so what was the point of what you said originally cuz that's basically what I do
Wdym two
I only have one reaper queue
one for freeing objects from high IPL and one for freeing thread stacks
or did I misunderstand
They can't be conflated like that because the kernel stack should be freed eagerly asap after the thread terminates and you also need to execute some kind of barrier to ensure the thread has really switched off its stack before you do it (for me that's taking and releasing the thread spinlock)
i see
I mean you could probably find a way to make them the same thing I just think it'd be unergonomic and weird
Is mintia not a Unix?
no
will mintia have something like io uring ?
damn
as far as thread reaping i think linux doesnt refcount it and does the waitpid thing
how does it handle SA_NOCLDWAIT then?
See no reason why it can't be
It'll be one with the Linux compat layer
it'll even have cow fork implemented the uvm way
The constraint of needing to have good fork and support Linux mount semantics (including bind mounts and so on) has dictated a lot of the planned design for the vmm and "vfs" respectively
I like how trying to design something well, informed by lots of different kernels, is antithetical to unix
It has to suck ass to count as unix
then either macos' kernel is not unix or it sucks
I think he's talking from the viewpoint of some people here
I was thinking of doing an r/osdev post when I got to userspace
But people are so idiotic on there that I can definitely do it earlier with little to show and still get attention
So I might do that today
put emphasis on the "I made everything from scratch" part
take this from the reddit bait master (master baiter?)
like "my own SMP paged kernel written in my own language running on my own virtual computer" or something
slacked off or what?
combination of school/tuition stress and the absolute worst boyfriend ever
i see
hopefully now that the summer is here you don't have the former problem (temporarily)
oh, ok
mintia2 is a year old now and its much further than old mintia was 12 months in
but not nearly as far as i would have liked
lol nvm
i think old mintia was slightly ahead
old mintia was at userspace, demand paging, etc
this was 1 year deep in old mintia
yeah because you had OSDLL already
oh you already had a shell going
interestingly its way fewer lines of code than mintia2 is now
around one year from when boron started i barely had a small IO system going, the nvme driver was working but not much more, and i was barely figuring out the memory manager
i'm 2 months short of being 2 years in my project (I started it on Aug 20, 2023) and right now i'm supposed to be working on libboron.so loading executables
actually theyre about equal
old mintia 12 months in: 43772 lines
new mintia 12 months in: 45720 lines
despite this, old mintia had userspace, demand paging, swapping, and a simple shell
mintia2 doesnt even have an IO system and the vmm is a stub
goes to show how much more enormous the mintia2 design is
second system effect...
1 yr in boron was
iprogramincpp@IPROGRAMINCPP2:/mnt/w/boron$ find -name *.c -or -name *.h | xargs wc -l
...
28902 total
iprogramincpp@IPROGRAMINCPP2:/mnt/w/boron$ gl
commit 392c8a9f2aa6473970e51e62cb47ac88e58288f7 (HEAD)
Author: iProgramInCpp <[email protected]>
Date: Sun Aug 18 23:01:50 2024 +0300
and now it's ```
iprogramincpp@IPROGRAMINCPP2:/mnt/w/boron$ find -name *.c -or -name *.h | xargs wc -l
...
38640 total
iprogramincpp@IPROGRAMINCPP2:/mnt/w/boron$ gl
commit 3adc4b0ba5392c07be96f0786ad321dd1328c479 (HEAD -> master, origin/master, origin/HEAD)
Author: iProgramInCpp [email protected]
Date: Sat Jun 28 23:35:57 2025 +0300
if i count the respective sdks though then its more like
old mintia 60k, new mintia 100k+
ppl always included the holyc compiler in line counts of templeos so i should start doing the same lol
sure
that puts me at a disadvantage because i can't bundle gcc and make with the loc count lmao
where's the progress stopped?
it hasnt really stopped (other than when i didnt have any time/energy irl) the pieces are just a lot bigger than they were back then
but the next major thing is the IO system
the IO system was totally static before but now its gonna be fully plug n play and power management aware
next immediate thing is to initialize the boot time IO catalog (iokit-y)
I will do an r/osdev post when I mount a filesystem
I don't wanna PRETEND
According to the roadmap I'll be p much at userspace when I have filesystem drivers
I can't do them until the vmm is like done
because of dependence on the file view cache which uses demand paged file mapping
its so crazy that a tradition honoring generals in a particular italian city state in like 200 BC led to North Korea having a triumphal arch
that just shouldnt be there man
the north korean one looks WAY cooler than any of the roman ones did too
much larger
tbf giant monuments tend to go pretty hard
thats like half the point of being a military dictator
i was reading a bit through mintia code and i don't really understand what's the point of user- vs kernel-mode waits? does it have any significance? or is it simply the initiator of the wait? there's also alertable vs non-alertable waits but i imagine this is about the possibility of waking up due to signals/apcs
Yeah there's a functional purpose
I should really do a table of the effects of alertability and wait mode
i think i've seen one for the windows kernel but i couldn't find it
but yes that would be very nice
It's not quite the same as the windows one
I yoinked the idea in 2021 but it has evolved independently since then
i just kinda assumed since the names looked similar
- i don't think windows does signals so that's one significant difference
it does SIGINT but it handles it weird compared to posix compliant OSes
that would make sense
It also has to do with whether the thread's kernel stack can be swapped to disk while it waits
And if you wait as usermode you're also making a promise to Ke that you're not holding certain locks
so that it can dispatch LAPCs in your context which can take said locks
(if you were holding them then you would deadlock)
forgot to post this
Note
SIGINT is not supported for any Win32 application. When a CTRL+C interrupt occurs, Win32 operating systems generate a new thread to specifically handle that interrupt. This can cause a single-thread application, such as one in UNIX, to become multithreaded and cause unexpected behavior.
LAPCs?
lazy APCs
ah
kernel mode apcs that arent dispatched unless
- youre in a usermode wait
- youre currently executing in usermode
- youre about to return to usermode
not to be confused with usermode APCs
these run in the kernel and are invisible to userspace
it just uses usermode as a quiescent point to know that certain locks arent held so that LAPCs are safe to grab them
@sterile frost
this only applies if IPL < KI_IPL_APC at wait time
if IPL >= KI_IPL_APC then all events are masked off always
interesting
with better color coding and a key
@warm pine you might also find this interesting
meaning of wait mode & alertability combinations in mintia2
theres also a third alertability option which is KE_CANCEL_ON_KAPC which is only valid when supplied with KE_KERNEL_MODE and waiting at IPL >= KI_IPL_APC
it causes KAPCs to interrupt the wait, without being delivered
this is used by the turnstiles impl to defer KAPC delivery while waiting on a turnstile
// Wait on the turnstile event.
// This wait can be cancelled if a kernel APC arrives, but the kernel APC
// will not be delivered since we're at KI_IPL_APC. This allows us to
// clean up our usage of our turnstile, which lets the APC grab turnstile-
// -backed locks once it is actually delivered when we lower IPL.
alertable := KE_UNALERTABLE
IF ipl == KI_IPL_LOW THEN
// The lock was acquired at LOW IPL, so we should cancel the wait and
// clean up if KAPCs arrive.
alertable = KE_CANCEL_ON_KAPC
END
KeWaitForSingleObject (
KE_KERNEL_MODE, // waitmode
alertable, // alertable
NULLPTR, // timeout
&turnstile^.Event.Header, // object
)
added that
thats really smart will
to be fair ipls in nyaux would be cool but that'd be too complicated and introduce a lot of problems 
im sorry to hear that :c
have hug
🫂
well i stole most of it
i mostly said that for comedic value and not as a venting thing
the bad boyfriend is real though
or was, i dumped his ass
thats still not okay :c
thats good
im changing a really gigantic macro at the start of the wait functions into a real function to see how many bytes of code that saves, which should be a little easier on icache at the expense of the extra function call
before:
text:
Size 64756 bytes
after:
text:
Size 63892 bytes
saved 864 bytes
i think thats worth it probably
thats 216 instructions
on fox32 it goes from 67456 bytes to 66556 bytes which is a 900 byte reduction
(no certain instruction count bc fox32 has var length encoding)
it cut the size of the wait routines in half
expense is an extra like 30 instructions
also i think i know a way to skip the check for interrupting events
if none are pending
(common case)
basically just have a little bitmap in the thread structure that has a bit flag for each pending event (one bit each for non-empty kapc, lapc, uapc queue, one bit for non-empty signal set, and so on)
check the whole bitmap at once and if its zero skip the check
should get me back the 30 instructions i lost by splitting out this function, and then some
yeah thatll save about 80 instructions on the hot path lol
80-30 = net improvement of 50
not very high tech to measure performance by instruction count but its a 1989 RISC so its probably good enough
the rly exciting thing is that youll probably rewrite nyaux one day and then i can ipl-pill you
cuz everyone does like 5 rewrites and each one is better than the last
(me included, im doing my fifth one rn)

well im not you will id rather reimplement like half of the codebase then rewrite the entire project from scratch 
well ya u can keep what u can
yea cause with this rewrite i am on currently im aiming for wayland and xorg
if i get there
idk
id probs rewrite like most of the code related to the VMM especially
and probs abstract cpu archiectures better
but yk yk
i also kinda want swap and shit so a VMM rewrite is on the cards
coincidentally my project is also the fifth in my OS development series
how is your project doing
this was a comment i wrote 5 yrs ago when i was afraid of swapping
"nanoshell minus one" (2019-2020), "nanoshell zero" (2021), nanoshell (2021-2023), nanoshell64 (2022), and boron (2023-2025)
(the first three were all called nanoshell but i added those names retroactively cuz they're not the real deal)
i am very much afraid of swapping so im you like 5 years ago lol (expect not gay)
right now i should be implementing the ELF loader in libboron.so
i made a simple read-only ext2 implementation a week or so ago
truly identical in all other respects
real
after implementing the ELF loader I'm probably going to work on a pseudo-terminal device
or driver
i haven't decided if it'll be part of the kernel or not
i lean towards it not being part of the kernel and instead being a driver
how many instructions would be too much for you ?
then i'll have to work on, like, everything. handle inheritance, fork, and then write support for basically everything in the MM/IO stack
also i just realized i can make KiWaitHead back into a macro and have it just do the initial common setup parts before doing something like this
... initial wait set up junk ...
IF thread^.PendingEvents THEN
KiWaitHeadLong ( thread, ipl, da da da )
END
and get the best of both worlds
that is EXTREMELY situation dependent lol
I just saved two instructions in a function that's inlined 20 times in my kernel
Work is done and I'm happy
"any more than whats required" is too much but it also depends on other factors
like if saving a few instructions is possible but would make the code unreadable and its a fairly uncommon codepath or one thats inherently slow anyway (like if it usually does an IO wait)
ill probably just not
also my one-man compiler sucks and does dumb codegen all the time anyway
i only obsess over it if its a really hot codepath
i can get it to contort into generating good code and stuff
or give up and use asm
can u give any more info on what u did exactly or is it top secret
ill have to do this tantalizing idea later i have to study for an exam tmrw
i was considering against it because i thought id have to update the little bit set with atomics but every place i need to update it, im already holding the thread spinlock
same with the place i check it
so i should just bea ble to access it normally
u might think "but will shouldnt you be implementing your io system instead of micro-optimizing stuff"
its part of my normal cycle i tend to oscillate between implementing new functionality and going over the old stuff with a fine toothed comb its just whatever i want to do at any given moment
i also think with a project this large and robustness being a goal
if i didnt do that it would collapse under its own weight
continually revisiting and improving old stuff is def necessary i think, and thats something i realized yrs ago while doing old mintia
i don't remember is it open ?
event bits are needed for:
- non-empty KAPC queue
- non-empty LAPC queue
- non-empty UAPC queue
SignalMask & SignalAcceptMaskis non-zero
if any of those are set then the full check will be performed otherwise itll be skipped
those bits will also be used to accelerate the check
stuff i cant do tonight or probably tmrw but should finish asap:
- thread event bits to avoid calling KiWaitCheck
- compiler support for simple inline asm for use for memory barriers and
pauserather than the goofy little subroutines i use rn
my favorite thing about my project is when i have to add a compiler feature to make something about the kernel suck less
makes me feel like a multics programmer in 1966
sounds like NT :^)
managed to fully inline the following (name, codepath savings for each usage):
KeMemoryBarrier, 2 instructions
KeWriteMemoryBarrier, 2 instructions
KeSpinPause, 2 instructions
KiReleaseSpinlock, 3 instructions
managed to partly inline the following:
KiReleaseSpinlockLower, 1 instruction
partly inlining KiReleaseSpinlockLower had the additional benefit of reducing cache footprint by reusing the cache line for KiLowerIpl, which was previously inlined specially in a handwritten asm routine
well, all of them removed a cache line of footprint each
idk how much of an impact this truly had on performance but it sure feels good
"why dont you benchmark!!!!" i think the results for this would be mostly lost in the noise of the emulator's performance inaccuracy
also while looking at the resulting asm
i noticed excess memory barriers
this appears twice in KeDispatch.jkl:
KiReleaseReadyQueueElevated ( prb )
KiReleaseThreadElevated ( current )
in each case it results in this:
wmb
mov long [s0 + 16], 0
wmb
mov long [s1 + 48], 0
which could just be
wmb
mov long [s0 + 16], 0
mov long [s1 + 48], 0
so im gonna add a KiReleaseTwoSpinlocks macro which does that
there are several other places where this happens
two spinlocks being released immediately adjacent which results in an unnecessary memory barrier
im not missing anything due to 3am brain right @dense vigil
ive designated you to tell me whether this is subtly broken
wmb is a store barrier so here its making sure that whatever you did in the critical section while holding the spinlocks is committed before you release them (if it got reordered after that or vice versa thatd be bad)
but it doesnt matter what order the spinlocks are released in or whatever so there doesnt need to be a second one