aboutsummaryrefslogblamecommitdiff
path: root/src/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
blob: 9c196810c351138fc45ea7d0cb241d8fcd12caf3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                    
                             
             
                     
                                 
                                          

                          
                                       
 
                                                      
         
                               
         
                                                                                                       
         
                            
                                                  
 
                                             
 
                                                                    
                                                                 
             
                                                  


                             
                                    
 
                                                 
 
                                      
             
                             
             
                                                 
 
                              
             
                                                                    
 
                                                   
             
                                                        
             
                                                 
             
                                                    
 
                                                

                
                                                                                                                           
 
                                                                                                    
 
                                                                     
                 
                                                                                       
                 
                                                   
                                                     
 
                                                                  
 
                                
                 
                                                                                          
                 
                                                 
 
                                                  
 
                                
                 
                                                                                   
                 
                                                 
 
                                                     
 
                                 
 
                                                                     
                 
                                                                          
 
                                                                     
                     
                                            
                     
                                                                                 
             
                                             
 
                          
         
                                                                
         
                                             
 
                                     
             
                                                                            
 
                                    
                 
                                                
 
                                                                                                  
                     
                                                       
                                                              
 
                                                                    
                     
                                     

                 
                                             

         
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.Horizon.Common;
using System;
using System.Buffers;
using System.Collections.Generic;

namespace Ryujinx.HLE.HOS.Kernel.Threading
{
    class KSynchronization
    {
        private KernelContext _context;

        public KSynchronization(KernelContext context)
        {
            _context = context;
        }

        public Result WaitFor(Span<KSynchronizationObject> syncObjs, long timeout, out int handleIndex)
        {
            handleIndex = 0;

            Result result = KernelResult.TimedOut;

            _context.CriticalSection.Enter();

            // Check if objects are already signaled before waiting.
            for (int index = 0; index < syncObjs.Length; index++)
            {
                if (!syncObjs[index].IsSignaled())
                {
                    continue;
                }

                handleIndex = index;

                _context.CriticalSection.Leave();

                return Result.Success;
            }

            if (timeout == 0)
            {
                _context.CriticalSection.Leave();

                return result;
            }

            KThread currentThread = KernelStatic.GetCurrentThread();

            if (currentThread.TerminationRequested)
            {
                result = KernelResult.ThreadTerminating;
            }
            else if (currentThread.SyncCancelled)
            {
                currentThread.SyncCancelled = false;

                result = KernelResult.Cancelled;
            }
            else
            {
                LinkedListNode<KThread>[] syncNodesArray = ArrayPool<LinkedListNode<KThread>>.Shared.Rent(syncObjs.Length);

                Span<LinkedListNode<KThread>> syncNodes = syncNodesArray.AsSpan(0, syncObjs.Length);

                for (int index = 0; index < syncObjs.Length; index++)
                {
                    syncNodes[index] = syncObjs[index].AddWaitingThread(currentThread);
                }

                currentThread.WaitingSync   = true;
                currentThread.SignaledObj   = null;
                currentThread.ObjSyncResult = result;

                currentThread.Reschedule(ThreadSchedState.Paused);

                if (timeout > 0)
                {
                    _context.TimeManager.ScheduleFutureInvocation(currentThread, timeout);
                }

                _context.CriticalSection.Leave();

                currentThread.WaitingSync = false;

                if (timeout > 0)
                {
                    _context.TimeManager.UnscheduleFutureInvocation(currentThread);
                }

                _context.CriticalSection.Enter();

                result = currentThread.ObjSyncResult;

                handleIndex = -1;

                for (int index = 0; index < syncObjs.Length; index++)
                {
                    syncObjs[index].RemoveWaitingThread(syncNodes[index]);

                    if (syncObjs[index] == currentThread.SignaledObj)
                    {
                        handleIndex = index;
                    }
                }

                ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray);
            }

            _context.CriticalSection.Leave();

            return result;
        }

        public void SignalObject(KSynchronizationObject syncObj)
        {
            _context.CriticalSection.Enter();

            if (syncObj.IsSignaled())
            {
                LinkedListNode<KThread> node = syncObj.WaitingThreads.First;

                while (node != null)
                {
                    KThread thread = node.Value;

                    if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
                    {
                        thread.SignaledObj   = syncObj;
                        thread.ObjSyncResult = Result.Success;

                        thread.Reschedule(ThreadSchedState.Running);
                    }

                    node = node.Next;
                }
            }

            _context.CriticalSection.Leave();
        }
    }
}