Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Multithreading #1610

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6bef4e1
Add changes from @Og-Rok
valentinbreiz Dec 27, 2020
63b4fcc
Merge master.
valentinbreiz Jun 6, 2022
e9e32aa
Merge branch 'master' into impl-multitask
valentinbreiz Jun 6, 2022
f2716cf
Fix conflicts + Lock memory gate using new memory manager
valentinbreiz Jun 6, 2022
7f1d9f7
Merge branch 'impl-multitask' of https://github.com/CosmosOS/Cosmos i…
valentinbreiz Jun 6, 2022
d7f5026
Remove useless code
valentinbreiz Jun 6, 2022
c3540b0
Fix build
valentinbreiz Jun 6, 2022
72c6861
Merge branch 'master' into impl-multitask
valentinbreiz Jun 9, 2022
727a090
Add Threading test
valentinbreiz Jun 9, 2022
30a1b98
Update test
valentinbreiz Jun 9, 2022
aeacb31
Update sln + csproj
valentinbreiz Jun 9, 2022
52e5308
Update test
valentinbreiz Jun 9, 2022
0b263d9
Merge branch 'master' into impl-multitask
valentinbreiz Jun 19, 2022
e48cda5
Merge branch 'master' into impl-multitask
MishaTy Jan 29, 2023
ca9bf96
Merge branch 'master' into impl-multitask
valentinbreiz Dec 26, 2023
68dc3e0
🐛 Fix memory manager with multithreading
valentinbreiz Dec 26, 2023
0c5fdfd
🐛 Fix IO ports call (now static call)
valentinbreiz Dec 26, 2023
66fe85b
🐛 Fix build
valentinbreiz Dec 26, 2023
d36bfd4
🐛 Fix build (wrong mStackContext path)
valentinbreiz Dec 26, 2023
ebe5302
Merge branch 'master' into impl-multitask
valentinbreiz Jan 7, 2024
7eec507
Merge branch 'master' into impl-multitask
valentinbreiz Jan 17, 2024
8ae051b
✨ Use local APIC timer instead of PIT for scheduler
valentinbreiz Jan 17, 2024
5eb0fe7
Merge remote-tracking branch 'origin/fix/bochsClock' into impl-multitask
valentinbreiz Jan 17, 2024
fd497d1
🐛 Fix scheduler with local apic timer (now works)
valentinbreiz Jan 23, 2024
78f9e08
Merge branch 'master' into impl-multitask
valentinbreiz Jan 23, 2024
72c1436
⚡️ Set 100Hz instead of 1000Hz
valentinbreiz Jan 23, 2024
af2a785
Merge branch 'master' into impl-multitask
MishaProductions Jan 24, 2024
485c468
Merge branch 'master' into impl-multitask
valentinbreiz Jan 29, 2024
106ec6f
Merge branch 'master' into impl-multitask
zarlo Apr 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions source/Cosmos.Core/INTs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ public struct IRQContext {
[AsmMarker(AsmMarker.Type.Int_LastKnownAddress)]
private static uint mLastKnownAddress;

public static uint mStackContext;

/// <summary>
/// IRQ handlers.
/// </summary>
Expand Down Expand Up @@ -845,6 +847,8 @@ public static void Dummy() {
HandleInterrupt_47(ref xCtx);
HandleInterrupt_48(ref xCtx);
HandleInterrupt_49(ref xCtx);
Processing.ProcessorScheduler.SwitchTask();
Processing.ProcessorScheduler.EntryPoint();
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions source/Cosmos.Core/Memory/Old/Old_Heap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using System.Threading.Tasks;
using Cosmos.Core;
using Cosmos.Core.Processing;
using Cosmos.Debug.Kernel;

namespace Cosmos.Core.Memory.Old
Expand All @@ -19,6 +20,8 @@ public static unsafe class Heap

private static uint mLastEntryIndex = 0u;

private static Mutex mMemeoryGate = new Mutex();

private static bool mInitialized = false;

private static void DoInitialize(uint aEndOfRam)
Expand Down Expand Up @@ -59,6 +62,11 @@ public static uint MemAlloc(uint aLength)
{
EnsureIsInitialized();

if (mMemeoryGate != null)
{
mMemeoryGate.Lock();
}

DataLookupTable* xCurrentTable = GlobalSystemInfo.GlobalInformationTable->FirstDataLookupTable;
DataLookupTable* xPreviousTable = null;
uint xResult;
Expand All @@ -81,6 +89,11 @@ public static uint MemAlloc(uint aLength)
}
}

if (mMemeoryGate != null)
{
mMemeoryGate.Unlock();
}

return xResult;
}
mLastTable = xPreviousTable;
Expand Down Expand Up @@ -117,6 +130,12 @@ public static uint MemAlloc(uint aLength)
}
mLastTable = xNextTablePointer;
mLastEntryIndex = 0;

if (mMemeoryGate != null)
{
mMemeoryGate.Unlock();
}

return xResult;
}
finally
Expand All @@ -129,6 +148,10 @@ public static uint MemAlloc(uint aLength)
{
//Debugger.DoSend(" Not enabling interrupts, because they weren't enabled yet!");
}
if (mMemeoryGate != null)
{
mMemeoryGate.Unlock();
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions source/Cosmos.Core/ObjUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using IL2CPU.API.Attribs;
using System;

namespace Cosmos.Core
{
public static unsafe class ObjUtilities
{
public static uint GetPointer(Delegate aVal)
{
return (uint)aVal.GetHashCode();
}

[PlugMethod(PlugRequired = true)]
public static uint GetPointer(Object aVal) { return 0; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have

public static unsafe uint* GetPointer(object aObj) => throw null; // this is plugged
which does more or less the same?
If you need a uint we also have
public static unsafe uint GetSafePointer(object aObj)


[PlugMethod(PlugRequired = true)]
public static uint GetEntryPoint() { return 0; }
}
}
27 changes: 27 additions & 0 deletions source/Cosmos.Core/Processing/Mutex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using IL2CPU.API.Attribs;

namespace Cosmos.Core.Processing
{
public unsafe class Mutex
{
public int gate;

[PlugMethod(PlugRequired = true)]
public static void MutexLock(int* mtx) { }

public void Lock()
{
while (gate != 0) { }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should yield here so we dont waste as much cpu time

gate = 1;
/*fixed (int* p = &gate)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the commented code be removed?

{
MutexLock(p);
}*/
}

public void Unlock()
{
gate = 0;
}
}
}
154 changes: 154 additions & 0 deletions source/Cosmos.Core/Processing/ProcessContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Cosmos.Core.Processing
{
public static unsafe class ProcessContext
{
public enum Thread_State
{
ALIVE = 0,
DEAD = 1,
WAITING_SLEEP = 2,
PAUSED = 3
}

public enum Context_Type
{
THREAD = 0,
PROCESS = 1
}

public class Context
{
public Context next;
public Context_Type type;
public uint tid;
public string name;
public uint esp;
public uint stacktop;
public System.Threading.ThreadStart entry;
public System.Threading.ParameterizedThreadStart paramentry;
public Thread_State state;
public object param;
public int arg;
public uint priority;
public uint age;
public uint parent;
}

public const uint STACK_SIZE = 4096;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk, but 4096 bytes seems too small. maybe 16KiB?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kernel's stack is 320kib and thread's is 4kib. we need to extend stack

public static uint m_NextCID;
public static Context m_CurrentContext;
public static Context m_ContextList;

public static Context GetContext(uint tid)
{
/*for(int i = 0; i < m_ContextList.Count; i++)
{
if(m_ContextList[i].tid == tid)
{
return m_ContextList[i];
}
}*/
Context ctx = m_ContextList;
while (ctx.next != null)
{
if (ctx.tid == tid)
{
return ctx;
}
ctx = ctx.next;
}
if (ctx.tid == tid)
{
return ctx;
}
return null;
}

public static uint* SetupStack(uint* stack)
{
uint origin = (uint)stack;
*--stack = 0xFFFFFFFF; // trash
*--stack = 0xFFFFFFFF; // trash
*--stack = 0xFFFFFFFF; // trash
*--stack = 0xFFFFFFFF; // trash
*--stack = 0x10; // ss ?
*--stack = 0x00000202; // eflags
*--stack = 0x8; // cs
*--stack = ObjUtilities.GetEntryPoint(); // eip
*--stack = 0; // error
*--stack = 0; // int
*--stack = 0; // eax
*--stack = 0; // ebx
*--stack = 0; // ecx
*--stack = 0; // offset
*--stack = 0; // edx
*--stack = 0; // esi
*--stack = 0; // edi
*--stack = origin; //ebp
*--stack = 0x10; // ds
*--stack = 0x10; // fs
*--stack = 0x10; // es
*--stack = 0x10; // gs
return stack;
}

public static uint StartContext(string name, System.Threading.ThreadStart entry, Context_Type type)
{
Context context = new Context();
context.type = type;
context.tid = m_NextCID++;
context.name = name;
context.stacktop = GCImplementation.AllocNewObject(4096);
context.esp = (uint)SetupStack((uint*)(context.stacktop + 4000));
context.state = Thread_State.PAUSED;
context.entry = entry;
if (type == Context_Type.PROCESS)
{
context.parent = 0;
}
else
{
context.parent = m_CurrentContext.tid;
}
Context ctx = m_ContextList;
while (ctx.next != null)
{
ctx = ctx.next;
}
ctx.next = context;
return context.tid;
}

public static uint StartContext(string name, System.Threading.ParameterizedThreadStart entry, Context_Type type, object param)
{
Context context = new Context();
context.type = type;
context.tid = m_NextCID++;
context.name = name;
context.stacktop = GCImplementation.AllocNewObject(4096);
context.esp = (uint)SetupStack((uint*)(context.stacktop + 4000));
context.state = Thread_State.ALIVE;
context.paramentry = entry;
context.param = param;
if (type == Context_Type.PROCESS)
{
context.parent = 0;
}
else
{
context.parent = m_CurrentContext.tid;
}
Context ctx = m_ContextList;
while (ctx.next != null)
{
ctx = ctx.next;
}
ctx.next = context;
return context.tid;
}
}
}
102 changes: 102 additions & 0 deletions source/Cosmos.Core/Processing/ProcessorScheduler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Text;
using IL2CPU.API.Attribs;

namespace Cosmos.Core.Processing
{
public static unsafe class ProcessorScheduler
{
public static void Initialize()
{
var context = new ProcessContext.Context();
context.type = ProcessContext.Context_Type.PROCESS;
context.tid = ProcessContext.m_NextCID++;
context.name = "Boot";
context.esp = 0;
context.stacktop = 0;
context.state = ProcessContext.Thread_State.ALIVE;
context.arg = 0;
context.priority = 0;
context.age = 0;
context.parent = 0;
ProcessContext.m_ContextList = context;
ProcessContext.m_CurrentContext = context;

IOPort counter0 = new IOPort(0x40);
IOPort cmd = new IOPort(0x43);
Copy link
Member Author

@valentinbreiz valentinbreiz Dec 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Find a way to use another port or something else than Pit (APIC timer?)
With this using PC Speaker won't be possible and will hang the processor scheduler due to PIT conflicts

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use PIT implemented in Cosmos, not IOPort PIT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AsertCreator they are one in the same

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe you can implement the nanosecond time in RTC, and use RTC to approximate? or maybe (pure speculation, as a junior dev who knows nothing about low level) reimplement PIT wait to tally up the number of times IRQ 0 has been triggered since the os was powered on, and use this to wait x amount of time. in the case of the second option, it would be possible to implement a system where multiple things can use PIT wait at the same time. see osdev wiki.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RTC is too slow for scheduling and idk if we could make interrupts with that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact RTC may not be a bad idea until we have HPET or local APIC timers, it indeed can generate IRQs. I'll try, I hope this won't mess up date time

Copy link
Member Author

@valentinbreiz valentinbreiz Jan 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

01:35:10.769067         Msg: Text from kernel: SwitchTask
01:35:10.787021         Msg: Text from kernel: SwitchTask
01:35:10.802028         Msg: Text from kernel: SwitchTask
01:35:10.818118         Msg: Text from kernel: SwitchTask
01:35:10.834006         Msg: Text from kernel: SwitchTask
01:35:10.849588         Msg: Text from kernel: SwitchTask
01:35:10.865546         Msg: Text from kernel: SwitchTask

I can get no more than 60Hz but it's possible

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that function as a stopgap until APIC gets finished?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. I just looked at the commit and it appears y'all got the APIC timer working. Ok then.


int divisor = 1193182 / 25;
cmd.Byte = (0x06 | 0x30);
counter0.Byte = (byte)divisor;
counter0.Byte = (byte)(divisor >> 8);

IOPort pA1 = new IOPort(0xA1);
IOPort p21 = new IOPort(0xA1);
pA1.Byte = 0x00;
p21.Byte = 0x00;
}

public static void EntryPoint()
{
ProcessContext.m_CurrentContext.entry?.Invoke();
ProcessContext.m_CurrentContext.paramentry?.Invoke(ProcessContext.m_CurrentContext.param);
ProcessContext.m_CurrentContext.state = ProcessContext.Thread_State.DEAD;
while (true) { } // remove from thread pool later
}

public static int interruptCount;

public static void SwitchTask()
{
interruptCount++;
if (ProcessContext.m_CurrentContext != null)
{
ProcessContext.Context ctx = ProcessContext.m_ContextList;
ProcessContext.Context last = ctx;
while (ctx != null)
{
if (ctx.state == ProcessContext.Thread_State.DEAD)
{
last.next = ctx.next;
break;
}
last = ctx;
ctx = ctx.next;
}
ctx = ProcessContext.m_ContextList;
while (ctx != null)
{
if (ctx.state == ProcessContext.Thread_State.WAITING_SLEEP)
{
ctx.arg -= 1000 / 25;
if (ctx.arg <= 0)
{
ctx.state = ProcessContext.Thread_State.ALIVE;
}
}
ctx.age++;
ctx = ctx.next;
}
ProcessContext.m_CurrentContext.esp = INTs.mStackContext;
tryagain:;
if (ProcessContext.m_CurrentContext.next != null)
{
ProcessContext.m_CurrentContext = ProcessContext.m_CurrentContext.next;
}
else
{
ProcessContext.m_CurrentContext = ProcessContext.m_ContextList;
}
if (ProcessContext.m_CurrentContext.state != ProcessContext.Thread_State.ALIVE)
{
goto tryagain;
}
ProcessContext.m_CurrentContext.age = ProcessContext.m_CurrentContext.priority;
INTs.mStackContext = ProcessContext.m_CurrentContext.esp;
}
Global.PIC.EoiMaster();
Global.PIC.EoiSlave();
}
}
}
Loading