aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs
blob: 841d0d69b1aa07be1f6a6bfb4b38941c768a7361 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using ChocolArm64;
using System.Threading;

namespace Ryujinx.HLE.HOS.Kernel.Threading
{
    class KCriticalSection
    {
        private Horizon _system;

        public object LockObj { get; private set; }

        private int _recursionCount;

        public KCriticalSection(Horizon system)
        {
            _system = system;

            LockObj = new object();
        }

        public void Enter()
        {
            Monitor.Enter(LockObj);

            _recursionCount++;
        }

        public void Leave()
        {
            if (_recursionCount == 0)
            {
                return;
            }

            bool doContextSwitch = false;

            if (--_recursionCount == 0)
            {
                if (_system.Scheduler.ThreadReselectionRequested)
                {
                    _system.Scheduler.SelectThreads();
                }

                Monitor.Exit(LockObj);

                if (_system.Scheduler.MultiCoreScheduling)
                {
                    lock (_system.Scheduler.CoreContexts)
                    {
                        for (int core = 0; core < KScheduler.CpuCoresCount; core++)
                        {
                            KCoreContext coreContext = _system.Scheduler.CoreContexts[core];

                            if (coreContext.ContextSwitchNeeded)
                            {
                                CpuThread currentHleThread = coreContext.CurrentThread?.Context;

                                if (currentHleThread == null)
                                {
                                    //Nothing is running, we can perform the context switch immediately.
                                    coreContext.ContextSwitch();
                                }
                                else if (currentHleThread.IsCurrentThread())
                                {
                                    //Thread running on the current core, context switch will block.
                                    doContextSwitch = true;
                                }
                                else
                                {
                                    //Thread running on another core, request a interrupt.
                                    currentHleThread.RequestInterrupt();
                                }
                            }
                        }
                    }
                }
                else
                {
                    doContextSwitch = true;
                }
            }
            else
            {
                Monitor.Exit(LockObj);
            }

            if (doContextSwitch)
            {
                _system.Scheduler.ContextSwitch();
            }
        }
    }
}