Przeglądaj źródła

Add exit thread

sunnycase 6 lat temu
rodzic
commit
bceb1d8e7e

+ 8 - 0
src/Chino.Chip.Emulator/ChipControl.cs

@@ -9,6 +9,8 @@ namespace Chino
 {
     public static class ChipControl
     {
+        public static TimeSpan DefaultTimeSlice => TimeSpan.FromMilliseconds(100);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern void Initialize();
 
@@ -21,6 +23,9 @@ namespace Chino
         [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern UIntPtr DisableInterrupt();
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern void SetThreadDescription(ref ThreadContextArch arch, string value);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern void RestoreInterrupt(UIntPtr state);
 
@@ -35,5 +40,8 @@ namespace Chino
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern void RestoreContext(in ThreadContextArch context);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public static extern void RaiseCoreNotification();
     }
 }

+ 1 - 0
src/Chino.Core/Threading/IThread.cs

@@ -6,5 +6,6 @@ namespace Chino.Threading
 {
     public interface IThread
     {
+        public string? Description { get; set; }
     }
 }

+ 30 - 28
src/Chino.Kernel/Program.cs

@@ -13,34 +13,36 @@ namespace Chino.Kernel
             KernelServices.Initialize();
 
             var scheduler = KernelServices.Scheduler;
-            scheduler.CreateThread(() =>
-            {
-                var terminal = Terminal.Default;
-
-                terminal.Foreground(TerminalColor.White)
-                    .WriteLine("Hello Chino OS!");
-
-                terminal.Foreground(TerminalColor.White)
-                    .Write("Used: ").Foreground(TerminalColor.Green).Write(Memory.MemoryManager.GetUsedMemorySize().ToString())
-                    .Foreground(TerminalColor.White).WriteLine(" Bytes");
-                terminal.Foreground(TerminalColor.White)
-                    .Write("Free: ").Foreground(TerminalColor.Green).Write(Memory.MemoryManager.GetFreeMemorySize().ToString())
-                    .Foreground(TerminalColor.White).WriteLine(" Bytes");
-                terminal.WriteLine().Reset();
-
-                var l = new List<int> { 1, 2, 3 };
-                foreach (var item in l)
-                    terminal.Write(item + ", ");
-                l.Clear();
-                terminal.WriteLine();
-                l.Add(4);
-                foreach (var item in l)
-                    terminal.Write(item + ", ");
-
-                terminal.WriteLine();
-                terminal.Ready();
-            });
-
+            var systemThread = scheduler.CreateThread(() =>
+              {
+                  var terminal = Terminal.Default;
+
+                  terminal.Foreground(TerminalColor.White)
+                      .WriteLine("Hello Chino OS!");
+
+                  terminal.Foreground(TerminalColor.White)
+                      .Write("Used: ").Foreground(TerminalColor.Green).Write(Memory.MemoryManager.GetUsedMemorySize().ToString())
+                      .Foreground(TerminalColor.White).WriteLine(" Bytes");
+                  terminal.Foreground(TerminalColor.White)
+                      .Write("Free: ").Foreground(TerminalColor.Green).Write(Memory.MemoryManager.GetFreeMemorySize().ToString())
+                      .Foreground(TerminalColor.White).WriteLine(" Bytes");
+                  terminal.WriteLine().Reset();
+
+                  var l = new List<int> { 1, 2, 3 };
+                  foreach (var item in l)
+                      terminal.Write(item + ", ");
+                  l.Clear();
+                  terminal.WriteLine();
+                  l.Add(4);
+                  foreach (var item in l)
+                      terminal.Write(item + ", ");
+
+                  terminal.WriteLine();
+                  terminal.Ready();
+              });
+
+            systemThread.Description = "System";
+            systemThread.Start();
             scheduler.Start();
         }
     }

+ 43 - 6
src/Chino.Kernel/Threading/IRQDispatcher.cs

@@ -10,14 +10,28 @@ namespace Chino.Threading
     public enum SystemIRQ : uint
     {
         SystemTick,
+        CoreNotification,
         COUNT
     }
 
-    public delegate void SystemIRQHandler(SystemIRQ irq, in ThreadContextArch context);
+    public class DPC
+    {
+        public object? Argument;
+        public DPCHandler Callback;
+    }
+
+    public delegate ref ThreadContextArch DPCHandler(object? argument, ref ThreadContextArch context);
+    public delegate ref ThreadContextArch SystemIRQHandler(SystemIRQ irq, ref ThreadContextArch context);
 
     public class IRQDispatcher
     {
         private readonly SystemIRQHandler?[] _systemIRQHandlers = new SystemIRQHandler?[(int)SystemIRQ.COUNT];
+        private readonly LinkedList<DPC> _dpcs = new LinkedList<DPC>();
+
+        public IRQDispatcher()
+        {
+            RegisterSystemIRQ(SystemIRQ.CoreNotification, OnCoreNotification);
+        }
 
         public void RegisterSystemIRQ(SystemIRQ irq, SystemIRQHandler? handler)
         {
@@ -25,21 +39,44 @@ namespace Chino.Threading
             Volatile.Write(ref _systemIRQHandlers[(int)irq], handler);
         }
 
-        internal void DispatchSystemIRQ(SystemIRQ irq, in ThreadContextArch context)
+        public void RegisterDPC(LinkedListNode<DPC> dpc)
+        {
+            using (ProcessorCriticalSection.Acquire())
+            {
+                _dpcs.AddLast(dpc);
+            }
+
+            ChipControl.RaiseCoreNotification();
+        }
+
+        internal void DispatchSystemIRQ(SystemIRQ irq, ref ThreadContextArch context)
         {
             Debug.Assert(irq < SystemIRQ.COUNT);
             var handler = Volatile.Read(ref _systemIRQHandlers[(int)irq]);
             if (handler != null)
-                handler(irq, context);
+                context = ref handler(irq, ref context);
             else
                 UnhandledIRQ(irq);
 
-            ExitIRQHandler();
+            ExitIRQHandler(context);
         }
 
-        private void ExitIRQHandler()
+        private void ExitIRQHandler(in ThreadContextArch context)
         {
-            ChipControl.RestoreContext(KernelServices.Scheduler.SelectedThread.Value.Thread.Context.Arch);
+            ChipControl.RestoreContext(context);
+        }
+
+        private ref ThreadContextArch OnCoreNotification(SystemIRQ irq, ref ThreadContextArch context)
+        {
+            while (_dpcs.Count != 0)
+            {
+                var item = _dpcs.First;
+                _dpcs.RemoveFirst();
+
+                context = ref item!.Value.Callback(item.Value.Argument, ref context);
+            }
+
+            return ref context;
         }
 
         private void UnhandledIRQ(SystemIRQ irq)

+ 61 - 17
src/Chino.Kernel/Threading/Scheduler.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Threading;
 using Chino.Chip;
 using Chino.Collections;
 
@@ -10,39 +11,59 @@ namespace Chino.Threading
     {
         private readonly LinkedList<ThreadScheduleEntry> _readyThreads = new LinkedList<ThreadScheduleEntry>();
         private readonly LinkedList<ThreadScheduleEntry> _suspendedThreads = new LinkedList<ThreadScheduleEntry>();
-        private LinkedListNode<ThreadScheduleEntry>? _runningThread = null;
-        private LinkedListNode<ThreadScheduleEntry> _selectedThread;
+        private volatile LinkedListNode<ThreadScheduleEntry>? _runningThread = null;
+        private volatile LinkedListNode<DPC> _removalThreadDPC;
         private readonly Thread _idleThread;
-        private bool _isRunning;
+        private volatile bool _isRunning;
 
-        public TimeSpan TimeSlice { get; private set; } = TimeSpan.FromMilliseconds(10);
+        public TimeSpan TimeSlice { get; private set; } = ChipControl.DefaultTimeSlice;
 
-        public LinkedListNode<ThreadScheduleEntry> SelectedThread => _selectedThread;
+        public LinkedListNode<ThreadScheduleEntry> RunningThread => _runningThread!;
 
         public ulong TickCount { get; private set; }
 
         public Scheduler()
         {
+            _removalThreadDPC = new LinkedListNode<DPC>(new DPC { Callback = OnRemoveThreadDPC });
             _idleThread = CreateThread(IdleMain);
+            _idleThread.Description = "Idle";
+        }
+
+        public void StartThread(Thread thread)
+        {
+            if (Interlocked.CompareExchange(ref thread.Scheduler, this, null) == null)
+            {
+                AddThreadToReadyList(thread);
+            }
         }
 
         public Thread CreateThread(System.Threading.ThreadStart start)
         {
             var thread = new Thread(start);
-            AddThreadToReadyList(thread);
             return thread;
         }
 
+        public void KillThread(Thread thread)
+        {
+            Debug.Assert(thread.Scheduler == this);
+            if (Interlocked.Exchange(ref thread.Scheduler, null) != null)
+            {
+                RemoveThreadFromReadyList(thread);
+            }
+        }
+
         public void Start()
         {
             Debug.Assert(!_isRunning);
 
+            _idleThread.Start();
+            Debug.Assert(_readyThreads.First != null);
+
             _isRunning = true;
-            Debug.Assert(_selectedThread != null);
-            _runningThread = _selectedThread;
+            _runningThread = _readyThreads.First;
             KernelServices.IRQDispatcher.RegisterSystemIRQ(SystemIRQ.SystemTick, OnSystemTick);
             ChipControl.SetupSystemTimer(TimeSlice);
-            ChipControl.StartSchedule(_selectedThread.Value.Thread.Context.Arch);
+            ChipControl.StartSchedule(_runningThread.Value.Thread.Context.Arch);
 
             // Should not reach here
             while (true) ;
@@ -53,22 +74,45 @@ namespace Chino.Threading
             using (ProcessorCriticalSection.Acquire())
             {
                 _readyThreads.AddLast(thread.ScheduleEntry);
+            }
+        }
 
-                if (_selectedThread == null)
-                    _selectedThread = thread.ScheduleEntry;
+        private void RemoveThreadFromReadyList(Thread thread)
+        {
+            using (ProcessorCriticalSection.Acquire())
+            {
+                if (_runningThread == thread.ScheduleEntry)
+                {
+                    Debug.Assert(_removalThreadDPC.Value.Argument == null);
+                    _removalThreadDPC.Value.Argument = thread.ScheduleEntry;
+                }
+                else
+                {
+                    _readyThreads.Remove(thread.ScheduleEntry);
+                }
             }
         }
 
-        private void OnSystemTick(SystemIRQ irq, in ThreadContextArch context)
+        private ref ThreadContextArch OnRemoveThreadDPC(object argument, ref ThreadContextArch context)
+        {
+            Debug.Assert(argument != null);
+            var thread = (LinkedListNode<ThreadScheduleEntry>)argument;
+            _readyThreads.Remove(thread);
+
+            if (_runningThread == thread)
+                return ref _readyThreads.First!.Value.Thread.Context.Arch;
+            return ref context;
+        }
+
+        private ref ThreadContextArch OnSystemTick(SystemIRQ irq, ref ThreadContextArch context)
         {
             Debug.Assert(_runningThread != null);
-            Debug.Assert(_runningThread.Next != null);
             Debug.Assert(_readyThreads.First != null);
 
-            var nextThread = _runningThread.Next;
-            if (nextThread == null)
-                nextThread = _readyThreads.First;
-            _selectedThread = nextThread;
+            var nextThread = _runningThread.Next ?? _readyThreads.First;
+            _runningThread = nextThread;
+            Debug.Assert(_runningThread != null);
+            return ref nextThread.Value.Thread.Context.Arch;
         }
 
         private void IdleMain()

+ 27 - 4
src/Chino.Kernel/Threading/Thread.cs

@@ -1,19 +1,34 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using System.Threading;
 using ThreadStart = System.Threading.ThreadStart;
 
 namespace Chino.Threading
 {
     public class Thread : IThread
     {
-        private Scheduler _scheduler;
         private readonly ThreadStart _start;
+        private object? _startArg;
 
         public ThreadContext Context;
+        public volatile Scheduler? Scheduler;
 
         public LinkedListNode<ThreadScheduleEntry> ScheduleEntry { get; }
 
+        public int ExitCode { get; private set; }
+
+        private string? _description;
+        public string? Description
+        {
+            get => _description;
+            set
+            {
+                _description = value;
+                ChipControl.SetThreadDescription(ref Context.Arch, value);
+            }
+        }
+
         public Thread(ThreadStart start)
         {
             _start = start ?? throw new ArgumentNullException(nameof(start));
@@ -22,14 +37,22 @@ namespace Chino.Threading
             ChipControl.InitializeThreadContext(ref Context.Arch, this);
         }
 
-        public void Start(object? arg)
+        public void Start(object? arg = null)
+        {
+            _startArg = arg;
+            KernelServices.Scheduler.StartThread(this);
+        }
+
+        public void Exit(int exitCode)
         {
+            ExitCode = exitCode;
+            Scheduler?.KillThread(this);
         }
 
-        private static int ThreadMainThunk(Thread thread)
+        private static void ThreadMainThunk(Thread thread)
         {
             thread._start();
-            return 0;
+            thread.Exit(0);
         }
     }
 }

+ 40 - 11
src/Native/arch/emulator/schedule.cpp

@@ -6,6 +6,7 @@
 
 using namespace natsu;
 using namespace System_Runtime::System;
+using namespace System_Threading::System::Threading;
 using namespace Chino_Kernel::Chino;
 using namespace Chino_Kernel::Chino::Threading;
 using namespace Chino_Chip_Emulator::Chino;
@@ -17,22 +18,26 @@ namespace
 std::atomic<bool> g_interrupt_enabled(false);
 CRITICAL_SECTION g_interrupt_cs;
 Event g_interrupt_enabled_event(CreateEvent(nullptr, TRUE, FALSE, nullptr));
-TP_TIMER *g_system_timer;
+HANDLE g_system_timer;
+TimeSpan g_time_slice;
 
 Semaphore g_interrupt_count(CreateSemaphore(nullptr, 0, 10240, nullptr));
 std::atomic<bool> g_system_timer_int(false);
+std::atomic<bool> g_core_notifi_int(false);
 
 void suspend_thread(ThreadContextArch &context)
 {
+    //printf("S (%d)\n", (int)GetThreadId(context.NativeHandle._value));
     auto handle = (HANDLE)context.NativeHandle._value;
-    SuspendThread(handle);
+    THROW_WIN32_IF_NOT(SuspendThread(handle) != DWORD(-1));
     CONTEXT c { CONTEXT_ALL };
     THROW_WIN32_IF_NOT(GetThreadContext(handle, &c));
 }
 
 void resume_thread(ThreadContextArch &context)
 {
-    ResumeThread((HANDLE)context.NativeHandle._value);
+    //printf("R (%d)\n", (int)GetThreadId(context.NativeHandle._value));
+    THROW_WIN32_IF_NOT(ResumeThread((HANDLE)context.NativeHandle._value) != (DWORD)(-1));
 }
 
 void notify_interrupt()
@@ -40,10 +45,23 @@ void notify_interrupt()
     THROW_WIN32_IF_NOT(ReleaseSemaphore(g_interrupt_count.Get(), 1, NULL));
 }
 
-void system_timer_thunk(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_TIMER Timer)
+void system_timer_main(void *arg)
 {
-    g_system_timer_int.store(true, std::memory_order_relaxed);
-    notify_interrupt();
+    g_system_timer = CreateWaitableTimer(nullptr, FALSE, nullptr);
+    THROW_WIN32_IF_NOT(g_system_timer != INVALID_HANDLE_VALUE);
+
+    LARGE_INTEGER due_time;
+    due_time.QuadPart = TimeSpan::get_Ticks(g_time_slice);
+    THROW_WIN32_IF_NOT(SetWaitableTimer(g_system_timer, &due_time, (DWORD)TimeSpan::get_TotalMilliseconds(g_time_slice), nullptr, nullptr, FALSE));
+
+    while (true)
+    {
+        if (WaitForSingleObject(g_system_timer, INFINITE) == WAIT_ABANDONED)
+            break;
+
+        g_system_timer_int.store(true, std::memory_order_relaxed);
+        notify_interrupt();
+    }
 }
 
 void interrupt_sender_main(void *arg)
@@ -61,7 +79,7 @@ void interrupt_sender_main(void *arg)
 
                 ThreadContextArch context;
                 // Suspend running thread
-                auto running_thread_entry = KernelServices::_s_get_Scheduler()->_runningThread;
+                auto running_thread_entry = Scheduler::get_RunningThread(KernelServices::_s_get_Scheduler());
                 if (running_thread_entry)
                 {
                     auto running_thread = running_thread_entry->item->_Thread_k__BackingField;
@@ -153,10 +171,10 @@ void ChipControl::_s_StartSchedule(gc_ref<ThreadContextArch> context)
 
 void ChipControl::_s_SetupSystemTimer(TimeSpan timeSlice)
 {
-    FILETIME due_time = ticks_to_filetime(TimeSpan::get_Ticks(timeSlice));
-    g_system_timer = CreateThreadpoolTimer(system_timer_thunk, nullptr, nullptr);
-    THROW_WIN32_IF_NOT(g_system_timer);
-    SetThreadpoolTimer(g_system_timer, &due_time, (DWORD)TimeSpan::get_TotalMilliseconds(timeSlice), 0);
+    g_time_slice = timeSlice;
+    auto systimer_thrd = _beginthread(system_timer_main, 0, nullptr);
+    assert(systimer_thrd);
+    THROW_IF_FAILED(SetThreadDescription((HANDLE)systimer_thrd, L"System Timer"));
 }
 
 void ChipControl::_s_RestoreContext(gc_ref<ThreadContextArch> context)
@@ -164,3 +182,14 @@ void ChipControl::_s_RestoreContext(gc_ref<ThreadContextArch> context)
     // Run selected thread
     resume_thread(*context);
 }
+
+void ChipControl::_s_RaiseCoreNotification()
+{
+    g_core_notifi_int.store(true, std::memory_order_relaxed);
+    notify_interrupt();
+}
+
+void ChipControl::_s_SetThreadDescription(gc_ref<ThreadContextArch> arch, gc_obj_ref<String> value)
+{
+    THROW_IF_FAILED(SetThreadDescription(arch->NativeHandle._value, reinterpret_cast<PCWSTR>(&value->_firstChar)));
+}

+ 2 - 1
src/Native/arch/emulator/threading.cpp

@@ -15,7 +15,8 @@ namespace
 uint32_t __stdcall thread_main_thunk(void *arg)
 {
     gc_obj_ref<Thread> thread(reinterpret_cast<Thread *>(arg));
-    return Thread::_s_ThreadMainThunk(thread);
+    Thread::_s_ThreadMainThunk(thread);
+    return Thread::get_ExitCode(thread);
 }
 }
 

+ 6 - 0
src/Native/natsu.fcall.cpp

@@ -1,6 +1,9 @@
 #include "Chino.Kernel.h"
 #include <cmath>
 #include <cstring>
+#ifdef WIN32
+#include <Windows.h>
+#endif
 
 using namespace natsu;
 using namespace System_Private_CoreLib;
@@ -77,6 +80,9 @@ void Debug::_s_WriteLineCore(gc_obj_ref<String> message)
 void Debug::_s_FailCore(gc_obj_ref<String> message, gc_obj_ref<String> detailMessage)
 {
     Chino_Kernel::Chino::Kernel::KernelDebug::_s_Write(message);
+#ifdef WIN32
+    DebugBreak();
+#endif
 }
 
 void Buffer::_s_Memmove(gc_ptr<uint8_t> dest, gc_ptr<uint8_t> src, uint64_t len)

+ 11 - 0
src/Native/natsu.threading.cpp

@@ -28,6 +28,17 @@ int64_t Interlocked::_s_Exchange(gc_ref<int64_t> location1, int64_t value)
 #endif
 }
 
+gc_obj_ref<Object> Interlocked::_s_Exchange(gc_ref<gc_obj_ref<Object>> location1, gc_obj_ref<Object> value)
+{
+#if _MSC_VER
+    return gc_obj_ref<Object>(reinterpret_cast<Object *>(_InterlockedExchangePointer(
+        reinterpret_cast<void *volatile *>(location1.ptr_), reinterpret_cast<void *>(value.ptr_))));
+#else
+    return gc_obj_ref<Object>(reinterpret_cast<Object *>(__sync_lock_test_and_set(
+        reinterpret_cast<void *volatile *>(location1.ptr_), reinterpret_cast<void *>(value.ptr_))));
+#endif
+}
+
 int64_t Interlocked::_s_CompareExchange(gc_ref<int64_t> location1, int64_t value, int64_t comparand)
 {
 #if _MSC_VER

+ 4 - 3
src/Natsu.Compiler/Program.cs

@@ -18,10 +18,10 @@ namespace Natsu.Compiler
             @"..\..\..\..\..\out\bin\netcoreapp3.0\Chino.Core.dll",
             //@"..\..\..\..\..\out\bin\netcoreapp3.0\Chino.Chip.K210.dll",
             @"..\..\..\..\..\out\bin\netcoreapp3.0\Chino.Chip.Emulator.dll",
-            @"..\..\..\..\..\out\bin\netcoreapp3.0\System.Private.CoreLib.dll",
+            //@"..\..\..\..\..\out\bin\netcoreapp3.0\System.Private.CoreLib.dll",
             //@"..\..\..\..\..\out\bin\netcoreapp3.0\System.Collections.dll",
             //@"..\..\..\..\..\out\bin\netcoreapp3.0\System.Memory.dll",
-            @"..\..\..\..\..\out\bin\netcoreapp3.0\System.Runtime.dll",
+            //@"..\..\..\..\..\out\bin\netcoreapp3.0\System.Runtime.dll",
             //@"..\..\..\..\..\out\bin\netcoreapp3.0\System.Runtime.Extensions.dll",
             //@"..\..\..\..\..\out\bin\netcoreapp3.0\System.Diagnostics.Debug.dll",
             //@"..\..\..\..\..\out\bin\netcoreapp3.0\System.Runtime.InteropServices.dll",
@@ -1051,10 +1051,11 @@ namespace Natsu.Compiler
                 }
                 else if (method.Name == "BeginInvoke")
                 {
-                    writer.Ident(ident).WriteLine($"return natsu::null;");
+                    writer.Ident(ident).WriteLine($"::natsu::pure_call();");
                 }
                 else if (method.Name == "EndInvoke")
                 {
+                    writer.Ident(ident).WriteLine($"::natsu::pure_call();");
                 }
                 else
                 {

+ 10 - 0
src/Natsu.Compiler/TypeUtils.cs

@@ -130,6 +130,16 @@ namespace Natsu.Compiler
 
         public static string EscapeVariableTypeName(TypeSig fieldType, TypeDef declaringType = null, int hasGen = 0, IList<TypeSig> genArgs = null)
         {
+            if (fieldType.ElementType == ElementType.CModReqd)
+            {
+                var modifier = ((ModifierSig)fieldType).Modifier;
+                var modName = modifier.FullName;
+                if (modName == "System.Runtime.CompilerServices.IsVolatile")
+                {
+                    return $"::natsu::clr_volatile<{EscapeVariableTypeName(fieldType.Next, declaringType, hasGen, genArgs)}>";
+                }
+            }
+
             if (IsValueType(fieldType))
                 return EscapeTypeName(fieldType, declaringType, hasGen, genArgs, cppBasicType: true);
             else if (fieldType.IsGenericParameter)