Change Semaphore -> ThreadBlocker
This was not a semaphore, I just named it one because I didn't know
what semaphore was. I have meant to change this sooner, but it was in
no way urgent :D
Implement SMP events. Processors can now be sent SMP events through
IPIs. SMP events can be sent either to a single processor or broadcasted
to every processor.
PageTable::{map_page,map_range,unmap_page,unmap_range}() now send SMP
event to invalidate TLB caches for the changed pages.
Scheduler no longer uses a global run queue. Each processor has its own
scheduler that keeps track of the load on the processor. Once every
second schedulers do load balancing. Schedulers have no access to other
processors' schedulers, they just see approximate loads. If scheduler
decides that it has too much load, it will send a thread to another
processor through a SMP event.
Schedulers are currently run using the timer interrupt on BSB. This
should be not the case, and each processor should use its LAPIC timer
for interrupts. There is no reason to broadcast SMP event to all
processors when BSB gets timer interrupt.
Old scheduler only achieved 20% idle load on qemu. That was probably a
very inefficient implementation. This new scheduler seems to average
around 1% idle load. This is much closer to what I would expect. On my
own laptop idle load seems to be only around 0.5% on each processor.
Current context saving was very hacky and dependant on compiler
behaviour that was not consistent. Now we always use iret for
context saving. This makes everything more clean.
Scheduler now has its own data SchedulerQueue which holds active nad
blocking thread lists. This removes need for BAN/Errors.h and making
current thread separate element instead of iterator into linked list.
This makes it possible to have current_thread on each processor
instead of a global one in Scheduler.
This allows us to allocate processor stacks, and other per processor
structures dynamically in runtime. Giving processor stack to
ap_trampoline feels super hacky, but it works for now.
This function was used when processes could die at any point in time.
Now that processes can only die in known spots, we can be sure they
are not holding any locks. This allows much more performant locking.
Signal handling code was way too complex. Now everything is
simplified and there is no need for ThreadBlockers.
Only complication that this patch includes is that blocking syscalls
have to manually be made interruptable by signal. There might be some
clever solution to combat this is make this happen automatically.
When we want to kill a process, we mark its threads as Terminating
or Terminated. If the thread is in critical section that has to be
finished, it will be in Terminating state until done. Once Scheduler
is trying to execute Terminated thread it will instead delete it.
Once processes last thread is marked Terminated, the processes will
turn it into a cleanup thread, that will allow blocks and memory
cleanup to be done.
This can be called from anywhere and just causes the scheduler to
schedule the next thread. This is more efficient and verbose version
of Scheduler::set_current_thread_sleeping(0), since we don't have
to wake other threads or do other verifications.