#Nyaux
1 messages ยท Page 45 of 1
You can do adaptive spinning where if the owner is running on another processor you spin wait for a little bit for the mutex to be released
And if not then you block
yeah, but spinlocks should ideally also have mechanisms to prevent that if that kind of thing can happen
Yes, it's called "threads don't own spinlocks, cpus do"
Generally you block preemption (if not disable interrupts entirely) while waiting for a spinlock or holding one
^
in kernel
meow
Spinlocks are double bad in userspace
Just do this
but they are still useful as a simple synchronization mechanism
For guarding stuff you cannot guard with a blocking mutex yeah
Like the scheduler itself
Or locks that have to be taken by ISRs
Otherwise they r bad
but for small things, spinlocks could probably still be faster
you yield if you can't take it and there's less cost than blocking/unblocking threads
Under contention a blocking mutex with adaptive spinning is basically always better
And when not contended you can make it just as fast as grabbing a spinlock
I dont understand the diff between blocking and yielding
blocking is not letting task run untill someone explicitly unblocks it
so like a waitlist
meowmeowmeow
woof
๐ก

Ok, I've changed my opinion
(I'm a bit biased because my pthread_mutex_unlock() can fail with ENOMEM
)
I remember the pain I went through with only 8GB of RAM on my laptop
what is does meowing mean?
My first laptop had 512MB of RAM
So just don't use vscode and you'll be set
how long ago was that?
At that time, I would have been an infant or maybe not even born yet
I was 8 if I can count lol
vscode only uses like 500mb for me
actually that was one electron process
so that might be a lie
laughs in 14M used by vim
I use Kate
I used to use neovim, and others
(if you don't know what that is, it's the KDE fancy text editor)
vscode has just worked for me
If you have ram for electron
(tbh I also just use vscode and haven't bothered setting up anything else)
I use vscode since I can't be bothered setting clion up for this project (frostyos, not what this thread is for) or trying to setup neovim
iPhones have a little switch on their side which mutes the notifications 
(only good thing about them)
I spent 60% of my time using neovim configuring it
skill issue on my part
<insert furry copypasta>
okay guys
when i reach the end of squid game thread blocking ๐
in nyaux soon
while watching squid game i was thinking of how my threadblocking function would look like
void ThreadBlock(struct thread_t *towhichqueue); // will block the current thread (remove it from the run queue, throw it into the parameter queue and scheduler yield)
void ThreadReady(struct thread_t *thread); // will ready whatever thread it got in its parameter
i think this is a good way of doign that
also my alt key
i see a problem
is broken
whats up
u should tell me
the comment
"block the current thread (remove it from the run queue"
if its the current thread then surely its already running
and not on the run queue
oh
ill explain this
in my scheduler design i have 2 pointers
current thread and run queue
run queue keeps a list of ready threads and whatever the current thread selects it changes its state to running
it stays in the run queue however
thats how my scheduler works
so actually the comment is correct will
thats slightly odd
obos is the paragon of design
real
i think the main issue with doing that is, one you dont get the natural like fifo properties of popping threads from the head of a queue and then pushing them to the tail later
two it makes fine grained locking a bit harder
then maybe ill change it
to be fair
anyways ima watch squid game
๐
or well
continue to watch the episode
me when i read 1984
thanks
no it doesn't
oh ok
i was just saying shit idk anything about oboes
lol
what obos does is has a function to take a thread of the runqueue it is on ("block" it), and a function to put it back on a runqueue
then a layer of abstraction above that in the form of struct waitable_header, which allows for threads to wait on objects, and for threads to be signalled by other threads, if they have that object
if that makes sense
pmOS does it 
last episode everyone
its shit
wow
that was the ending
crazy
that was actually so ass
anyways
thread blocking time
and mutexes for nyaux
tf
crazy things happening in ze scheduler lol
these functions should work wtf
sorry just realized
im stupid
no nvm
oop
im not gonna do this
cause im struggling hard trying to get the queueing to work
its just easier :)
bro
hi
hey
i was punished for quacking ๐
time to learn more
nvm ill js do a single circular linked list
no point in having a double linked list
nvm
fine
ill do a double circular linked list
what is this
idfk
then why you leraning this
you got new pc or laptop ???
yay you fixed it
then idk
WHY CANT I GET CIRCULAR DOUBLELY LINKED LISTS RIGHT
UGH
whatever forget about it
no nvm
ill make a userspace program later
to get circular doublely linked lists right
for now im going to just take a break cause im rlly frustrated
what do you need a circular linked list for?
meow :3
Why dont you use linux's list.h (struct list_head)?
Its so nice to use
or the bsd ones?
It's gpl2
the bsd ones are bsd
Yeah, but the Linux one is gpl, that's what I was talking about
yes
I have an MIT one if you want very permissive
my bad been having a bad sleep schedule
slept til like
10pm
๐
just woke up
@kind root @finite summit whats yall opinion on this ass sleep schedule
i need to fix it
i dont know how
im good
i just need to write a userspace program, test it a bunch, put it into the kernel
like a good programmer would
even in userspace im screwing this up
๐
okokok i got it
time to impl this into kernel space
figured it out using cool double pointer
seems like it works
why is bro stuck now
funny
got it working :)
works with smp too
thread blocking time ig
will i sched yield on threadready
or no
well i have the functions anyway
its time to impl a kernel mutex thing
I wonder if you can pass some sort of callback to the unblocking thread 
why
more work
2 instructions
Why
The hell would you do that
so when the thread is dying or something it can do work in the context of new thread
Whatever happened to naming conventions
so like to free stack or something
oh u mean e and h
dw they still there

nyaux is back thats all that matters
yesss
damn feel like shit again
lmfao 18 videos on that
They're short tho
I'd have done a single big video
huuuuuuuuuuggggggg
bru
lol
todo list for today:
impl blocking threads and kmutex
see how i can impl schedule work and wait for work completion
then we are done
is the bool acquired used for checking if it was acquired?
yes
idk if it's the best impl
(like it's not)
*i'm probably not the best person to be asking things about concurrency
but it should work
who can i ask about concurrency
idk
@finite summit
what do you want to ask
like that impl is it bad?
for a kernel mutex
I don't think
it's ok but probably there are better implementations, which you probably don't really need
I've never written a futex so I might get it wrong
if you listen to me*
whats the better impl then
that's probably masters in computer engineering level of knowledge lol
what I meant is that its likely going to work just fine, its basically the same that I use except I have an event in place of the thread list + spinlock
dont know what an event is but okay
semaphore
a thing that you can signal and wait until someone signals it
mutex is theoretically just a binary semaphore
fair
actually yeah, idk why did I do my mutex like this on top of the semaphore 
having semaphores allows you to implement other forms of concurrency I think
well ima go impl my kmutex
and yeye
also
anyone got a better vscode theme
i really hate the one i have now
white theme 

So I think you lock the lock, do compare exchange on the address given by the user, if it succeeds you block the thread
the bool
one is to protect the waiter list
Wait, we're talking about kernel
yea
not futex
yea
ignore what I said
half-ignore
You take the mutex::lock, check if lock is acquired. If it isn't, you set it to true. If it is, you block the thread and push it to the list of waiters. Unblock the lock
You've said you've implemented circular lists, right?
yea
all atomic operations?
No
oh
Only mutex::lock
You can remove the lock and do it with atomics
But that's more complicated
Yes
void take_lock(struct mutex *him) {
spinlock_lock(&him->lock);
if (!ackquired) {
acquired = true;
spinlock_unlock(&him->lock);
} else {
// Push yourself to list of blocked threads
spinlock_unlock(&him->lock);
// Switch to some other thread
}
// Lock is taken
}```
Something like that
Yeah
fair
portuguese girl
shes very pretty :3
i love her
bold thing to say as a 13 year old
fake news
let him have fun before the adult life hits 
my birthdays in 2 months actually
@flat nymph what about releasing the lock, what exactly do u do
wake up the blocked threads?
i got 3 months until adulthood too imma kms
only at the front?
what about the other blocked threads
why
oh
You can probably change it and do while (1) here
or goto memes since it's c
it's up to you
its fine
nahh its fineeeeeeeeeeeee
you sched_yield in locks in userspace
for futex yea
kk
"reaper(): thread killed"
yea thats normal
it's killing random threads?
maybe your release function is broken?
mutex
looks inside
spinlock
threadready doesnt yield


but you should be yielding at one point, no?
have you tried: gdb
bruh
it hurts my stomach
oh
I thought you were gonna debug by looking at the code instead of gdb
kbye
๐
nvm ill do it now
i do
that's not how reference counting works
what
and thats not how reaping threads work
if (!(--obj->refcount))
free(obj);```
i do refcount_inc on create_kthread()
and refcount_dec on exit_thread()
ok
whats wrong with that
then in refcount_dec
you free when refcount gets to zero
not in thread x
you would do this in refcount_dec
wrong.
struct thread
{
//shit
atomic_size_t refcount;
};
void ref_thread(struct thread* obj)
{
obj->refcount++;
}
void unref_thread(struct thread* obj)
{
if (!(--obj->refcount))
free(obj);
}```
so im supposed to free the thread in the unref thread thing?
atomic_size_t
i dont have that
like huhh
in any case the bsd0 freestanding headers that the template pulls by default does not have it
either way, you can do _Atomic(size_t)
and where can i import _Atomic
it's a keyword
your free() can queue the thread for reaping
infy stop giving him bad ideas

thread reaper thread is a good idea
unless you mean free just schedules a DPC to actually free the thread at a lower priority
he said its a good idea @finite summit

I disagree
imo, memory is ideally released as soon as possible, unless you cannot because your IRQL/IPL/whatever is too high

are you being detained?
no its just my ed
ed?
eating disorder
releasing various resources of a giant process can take a lot of time
and you're stealing that time without an upper bound
then in that case, that kinda just falls into "unless you cannot because IRQL is too high"
then you can schedule a DPC to free everything
anyways i dont think i can think anymore without food, so ima watch something til i can eat
then we can get back to work
im even struggling to type lol
if you don't mind me asking, why 1:10am specifically, doctor said so or...
because i eat every 16 hours
i eat 10:10am so
8 hours behind is 1:10am
or
2:10am
but because im feeling extra hungry today im allowing myself to eat 1:10am
why would IPL be too high?
this is in a thread context
I mean you'd probably be at dispatch level
my exit_thread raises IRQL to dispatch
your skill issue does not apply to general cases
Once you remove a thread from a runqueue, it won't get run until it's back on a runqueue.
exit_thread removes the current thread from the runqueue
that's why I run mine at dispatch
yes you can take a lock
for like
removing the thread from the list?
that's the only point where you'd have high irql that I can think of
and thats like
a few operations
good point
so I don't see what your point is
or why that would affect having a reaper thread
because a reaper thread would basically mean taking another lock (reaper queue) for a short time to add the thread to that list
instead of freeing everything directly in exit() which would incur latency
if anything you may want freeing to be a high irql operation
so doing it there would just add more latency
Why have a reaper thread though if you can just push zombie to ready queue?
you mean doing the freeing when the scheduler encounters a zombie thread?
yes (?)
im starting to feel really sick
like dizzy
is this a bad sign
i checked my weight and im starting to lose a ton of weight again
just 2 hours chat
gotta stay strong
what stops you from eating
eating disorder
that does not answer my question
ok so you complain about feeling dizzy but you dont eat on purpose?
have you seen a doctor
yes
they know my issues and they did get me once to eat 2k cals, but now im doing something they dont like anymore
which is eating 1k
anyways id prefer we keep on topic to nyaux
i will debug the blocking threads issue shortly
as oberrow said it might be the release function
you started the topic tho 
fair 
my bad chat kinda overslept
now its working?
ok now its not working
okay
something very bad
it only works not under gdb?

oh nvm its random
ok it seems that whenever a thread has the lock the other thread dies
not fun

idk whats going on and it being rlly random makes it difficult
help

Real osdevs would never be in a relationship, its a distraction from osdev
yes
is factorio a distraction from osdev too?
yes !!!
yea
my bad chat
was sleeping then had to talk with my gf and play with her a bit
anyways i think the kprintfs are causing the random behavior
i have an idea
okay i see
we lost you ๐
๐ฆ๐
๐ฆ๐ญ
wdym
Wait a second
kk
okay

You need to be scheduling the next thread
And
What I was typing
CPU 1 CPU 2
ThreadBlock(stuff);
ThreadReady();
schedd(NULL):

im not saving the context
Then what are you doing
nvm i am
but
i have different cpu queues
so cpu 1 and cpu 2 dont even share the same thread
im protecting the blocked thread queue with a spinlock
But if threads get pushed to ready back on the CPU they were blocked on then it should be fine
yea it should
void acquire_kmutex(struct KMutex* mutex)
{ spinlock_lock(&mutex->protectionlock);
// kprintf("acquire_kmutex(): lock is %d\n", mutex->lock);
if (!mutex->lock) {
mutex->lock = true;
spinlock_unlock(&mutex->protectionlock);
return;
} else {
ThreadBlock(mutex->blockedthreadsqueue);
spinlock_unlock(&mutex->protectionlock);
schedd(NULL);
}
}
void release_kmutex(struct KMutex* mutex)
{
spinlock_lock(&mutex->protectionlock);
if (mutex->blockedthreadsqueue == NULL) {
mutex->lock = false;
spinlock_unlock(&mutex->protectionlock);
return;
} else {
struct thread_t *thread = pop_from_list(&mutex->blockedthreadsqueue);
ThreadReady(thread);
spinlock_unlock(&mutex->protectionlock);
}
}
2 of my kmutex functions
still dont understand why klocktest2 isnt getting unblocked

gdb not telling me anything
mutex->blockedthreadsqueue == NULL
Is never NULL with circular lists
Or they are not circular
it can be null
Also, what is your ThreadReady
Then you're doing it wrong 
no its just the nyaux way
wanna die chat
Push into queue -> pushes onto current CPU
Then this happens
how
that shouldnt happen
at all
Think about it
im protecting it with a spinlock tho
no 2 cpus will have the spinlock
so it SHOULD be fine
And?
its not gonna release the spinlock until its done blocking
idk how to explain it to you
bru
@finite summit am I insane
You are writing a microkernel, so ges
*yes
there are gradations of insanity
Could this happen with his code?
why are you mixing PascalCase with snake_case btw
Still
ThreadBlock(mutex->queue);
spinlock_unlock(mutex->lock);
spinlock_lock(mutex->lock);
ThreadReady(thread)
spinlock_unlock(mutex->lock);
// ...
schedd() -> schedules the thread
Thread context not saved kernel crashes
schedd(NULL);
dayum how do i fix this
you just reschedule
@surreal path is it true what you leave osdev because you have a gf and you will no more say hello to me ๐ฆ๐
wdym
reschedule when blocking the thread
but i do
so it's removed from the runqueue
idk your codebase enough

There are various solutions
Like mark your thread as ready inside the switch_to whatever it's called function
what
also even if i solve this
i still have this issue where klocktest2 never executers
executes*
gonna cry chat

cry in commits too
ignore the state = running i changed it to state = ready
my bad with that
bru
:(
poor nyaux... First ECDT now this
yea i need help from someone
codes on github
just gotta hope someone will take a look
cause im out of ideas

and i just wanna work on my vfs atp
mr @finite summit has bash already
and what does nyaux have
nothing lmao
You're missing a degree in computer science 
bru
degree in computer science is literally useless
hi mr qwinci
our lord and savior
I wouldnt say so
well for getting a job maybe but like otherwise
it depends
big brain idea for you, clone nyaux from github 
when i get stuck on some issues i try to rewrite it
used to be like that
but thats stupid
if you rewrite it MAYBE you find a problem
bru
I don't feel like looking at nyaux code rn lol
my brain isn't functional enough to design concurrency memes for nyaux
maybe later when u feel better?
nyaux have anti copypaste system
you don't understand this
when u do feel better please if u can try to help, for the meantime ill try to look at the code for a little more if i can spot something
it will be fun if someone copypaste from nyaux
blocked thread is not running
i.e. klocktest2 is not executing anything anymore after acquire
after getting blocked
even tho all the code makes sense
no klocktest2 i have the lock
shit
i'm saying he has a race condition
(though idk if it's what's manifesting in bug)
no its not
cause i solved it and bug isnt solved

no i mean
blocked threads don't run
yes ik
did you add it back to the list
This doesn't solve it
how im doing it in the scheduler
void ThreadReady(struct thread_t *thread) {
struct per_cpu_data* cpu = arch_get_per_cpu_data();
thread->state = READYING;
push_into_list(&cpu->run_queue, thread);
}
...
push_into_list(&cpu->run_queue, cpu->cur_thread);
cpu->cur_thread = pop_from_list(&cpu->run_queue);
if (cpu->cur_thread->state == READYING) {
cpu->cur_thread->state = READY;
push_into_list(&cpu->run_queue, cpu->cur_thread);
cpu->cur_thread = pop_from_list(&cpu->run_queue);
cpu->cur_thread->state = RUNNING;
return old;
}
...
bit hacky but it should
you need to be waiting
what
you need to be waiting.
bru
you need to be waiting...
you set thread to reasy in schedd or wherever
you take the address of a parameter
thats what im doing
the function that switches the context
and pushing the thread to there is not going to last very long
void ThreadBlock(struct thread_t *whichqueue) {
struct per_cpu_data* cpu = arch_get_per_cpu_data();
cpu->cur_thread->state = BLOCKED;
push_into_list(&whichqueue, cpu->cur_thread);
}
after you push the registers on stack and load different stack
when loading thread, you spin wait untill it's ready
wha
like you can't switch to a thread that's running on other CPU
So you either
- wait for it to not be running
- return it back to its CPU so it deals with it
if its not on the cpus run queue
well here you do push_into_list(&whichqueue, cpu->cur_thread); and whichqueue is NULL so you end up doing ```c
void ThreadBlock(struct thread_t whichqueue) {
struct per_cpu_data cpu = arch_get_per_cpu_data();
cpu->cur_thread->state = BLOCKED;
// what push_into_list(&whichqueue, cpu->cur_thread); does:
whichqueue = cpu->cur_thread;
whichqueue->next = whichqueue;
whichqueue->back = whichqueue;
}
you're moving the thread to a queue of different CPU (if I understood your code correctly) one you unblock the thread
to its own cpus queue
not of the queue of a different cpu?
yea and whats wrong with that?
im so confused lol
Look at this
I'm not talking about thread context
well how do you access the queue after that? its not stored anywhere, its the same as ```c
void ThreadBlock(struct thread_t whichqueue) {
struct per_cpu_data cpu = arch_get_per_cpu_data();
cpu->cur_thread->state = BLOCKED;
// what push_into_list(&whichqueue, cpu->cur_thread); does:
struct thread_t* dump = cpu->cur_thread;
dump->next = dump;
dump->back = dump;
}
you need to make ThreadBlock take an address of the queue not the first thing in the queue
When switching to a different context
He said he has circular lists
(which can be NULL for some reason)
oh because im not taking a double ptr
therefore only modifying the parameter
not the actual queue
or just make it a proper circular list
though well doing that with threads might not be the best thing ever as then you'd have to have a dummy thread entry inside kmutex for the start
lemme make it a double ptr
make lists store addresses of the list itself?
List head
Idk
That's what my circular lists do afair
So it's still just 2 pointers
that solved it but it kills itself
Yes because your circular lists are broken
how
with the way nyaux does it where its thread* instead of some kind of general list_head/list_entry that's likely not very good idea
Yeah
as you'd need a dummy thread at the start
He's missing some cursed macros
??
bros not answering my question
circular lists are just a bit easier
triple not answering
huh interesting, you define functions outside of the class in headers
Stop copy-pasting code and write (or steal) yourself a proper circular list
im not wtf
??????????
Rich freestanding C++ library. Contribute to nyx-os/atlas development by creating an account on GitHub.
here are mine
๐
@flat nymph im not copy pasting??????????????????????
pretty smart actualy
where did i copy paste
why?
i wrote the circular list myself
like wtf
i did not copy paste
easier to see which functions exist without having to look at the implementation
Also I have my own rb tree implementation ๐ https://gitlab.com/mishakov/pmos/-/blob/main/sysroot/include/pmos/containers/intrusive_bst.hh?ref_type=heads
(only a tiny bit cursed)
@flat nymph where did i copy paste
thats just a more awkward solution
u would press collapse 50 times
i find that very insulting
if theres 50 functions
Rich freestanding C++ library. Contribute to nyx-os/atlas development by creating an account on GitHub.
thank god this exists
I have too but its actually the frigg one just first written in a non-recursive way in c and then I ported that to c++ 
bro mishakov can u answer
because I am too dumb to understand how it works lol
I store color in pointers 
not a bad idea
What your circular lists are not circular lists
okay but they werne't copy and pasted
I implemented a hash array mapped trie which no one here has, so checkmate nerds ๐
i put freebsd's tree.h rbtree into c++, possibly it even works, it does the same:
https://gist.github.com/netbsduser/56a72833649e38fe2bd81c82b28012c4
it was my own impl
can u not attempt to say that again because i find that especially insulting
I mean it's not a stupid idea, but how much overhead does it have realistically? (I am coping)
why did u do that ๐
I want to eventually do rbtree without parent pointer though for struct Page
like bro
i have ze scheduler im just fucking dying
and like
i feel extremely insulted
plus kinda upset? weirdly. idk i had a bad day today
take out ur anger by fixing ur code
dont be so susceptible
no offense but literally every bug u encounter u instantly give up and ask other people to fix it
i wouldn't agree with the 2nd statement but
the first definitely true
ur mental is so fragile when it comes to coding and bugs
he's kinda right
?
i dont do that everytime
btw not tryna shit on u or whatever, legitimate remark
i know i say a bunch of mean things but this isnt meant to be one of them
im saying that because i want nyaux to succeed as well
u gotta know how to use gdb and printfs
i do
instead of giving up
use it harder
bro
yes
yes but why was qwinci able to debug it in like 5 mins
thats not it
no its because qwinci actually learned to solve bugs by debugging his stuff
Debugging is not something you are born with
thats not what i meant
Intelligence is dictated by your willingness to learn
yeah you just get better at it over time
like at all
no cause like no 1 why did miskaov immdeitly assume i copy pasted code like wtf. no 2 yes i get it its stupid im always not figuring out the simpliest of things but again im still new to osdev and i still TRIED to debug my own stuff that still counts no 3 all of yall saying this is kinda making me feel even worse not better. i get yall are trying to give me cristism but i already had a really shitty day today and im actually starting to cry cause i feel like nothing i did even mattered and yall just did the code yourselves and i didnt do any effort or smt. im not saying yall are trying to say this but that what it feels like to me rn
i still worked hard on this project
doesnt matter if i dont have the debugging skills of qwinci or anyone, i still did try to printf i still did try to gdb and im still learning
plus as qwinci said
you get better at this over time

im gonna ignore all of what you said and focus on no 2, you need to be good at programming, not at osdev
but ur saying this on the assumpation i didnt try and havent solved my own bugs previously
you tried for 5 mins and gave up is the problem
to which i did
what rustc said
this is an assumpation your making
i saw u try but u barely did so
osdev is just like "load gdt, set up page tables" but everything else is just regular programming but with weird constraints of no os
bro literally everytime you encounter a bug you do this
not really
kernel programming is advanced concurrent programming
yeah but still it's not specific to osdev
just do -smp 1 
i might have phrased that wrong because i had a bit more background to it
but i guess this is similar to how people think "oh how do i do X with limine"
that is not true
i had a really rough day today
and they dont realize that once the RIP is in their kernel limine does not do anything anymore and they are on their own
i agree i didnt try that hard
this has been a consistent pattern
yes it has, but that doesnt mean its all the time. and im trying to do less and less of it as time goes on
remember the first bugs i encountered at the start?
remember how i reacted?
compare to how i react now
well maybe i just dont know the full picture
i think the rust arc might have skewed the picture a bit lol
12 hours a day of ub and complaining
bro
not rlly
but ur back to C and that's all that matters
I didn't mean copy-paste as taking something from someone else, but by not having a single good list implementation
ur right on that but copy and paste kinda meant something else to me, try to phrase it differently next time your trying to criticise this
also i was crying earlier and now i feel like actually awful, i dont think im gonna work on anything anymore for today for my mental health. i dont wanna get more upset atm im on the verge of crying again lol
cant believe im crying over what strangers think of my code on the internet, man i really am pathetic lol
wish i wasnt like this im not going to lie
we didnt say anything about your code tho
just your approach to working with said code
more about my behavior yes
Your code is fine
Idk if I sounded too strong
But I believe that it's a good idea to just write a single good list implementation (and other containers) with C macros and not spend your time debugging it
i get what yall are trying to say to me anyway i just need to calm down and stop crying, i havent told yall this but i have an issue with being like really sensitive which sucks ass because i cry over the smallest things
yea i get what u were trying to say
makes it difficult especially in real life
i just went back to school today and shit wasnt nice
especially with the teachers shouting at me
yeah sorry if that sounded too rough
its fine, i just need to lie down and not think about anything rn
Like dude you're 17 and doing osdev, which is like one of the hardest domains in programming
if it makes u feel any better i didnt know any programming until i was 18
This was said with irony
i injoured my leg and arm today while coming back from the store
im in too much pain to do anything today
hopefully tmrw
Hope you get better!
thanks!
pls don't die
still need to heal


