KThread.cs 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Cpu;
  3. using Ryujinx.HLE.HOS.Kernel.Common;
  4. using Ryujinx.HLE.HOS.Kernel.Process;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Numerics;
  8. using System.Threading;
  9. namespace Ryujinx.HLE.HOS.Kernel.Threading
  10. {
  11. class KThread : KSynchronizationObject, IKFutureSchedulerObject
  12. {
  13. public const int MaxWaitSyncObjects = 64;
  14. private ManualResetEvent _schedulerWaitEvent;
  15. public ManualResetEvent SchedulerWaitEvent => _schedulerWaitEvent;
  16. public Thread HostThread { get; private set; }
  17. public ARMeilleure.State.ExecutionContext Context { get; private set; }
  18. public KThreadContext ThreadContext { get; private set; }
  19. public int DynamicPriority { get; set; }
  20. public long AffinityMask { get; set; }
  21. public long ThreadUid { get; private set; }
  22. private long _totalTimeRunning;
  23. public long TotalTimeRunning => _totalTimeRunning;
  24. public KSynchronizationObject SignaledObj { get; set; }
  25. public ulong CondVarAddress { get; set; }
  26. private ulong _entrypoint;
  27. private ThreadStart _customThreadStart;
  28. private bool _forcedUnschedulable;
  29. public bool IsSchedulable => _customThreadStart == null && !_forcedUnschedulable;
  30. public ulong MutexAddress { get; set; }
  31. public KProcess Owner { get; private set; }
  32. private ulong _tlsAddress;
  33. public ulong TlsAddress => _tlsAddress;
  34. public KSynchronizationObject[] WaitSyncObjects { get; }
  35. public int[] WaitSyncHandles { get; }
  36. public long LastScheduledTime { get; set; }
  37. public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
  38. public LinkedList<KThread> Withholder { get; set; }
  39. public LinkedListNode<KThread> WithholderNode { get; set; }
  40. public LinkedListNode<KThread> ProcessListNode { get; set; }
  41. private LinkedList<KThread> _mutexWaiters;
  42. private LinkedListNode<KThread> _mutexWaiterNode;
  43. public KThread MutexOwner { get; private set; }
  44. public int ThreadHandleForUserMutex { get; set; }
  45. private ThreadSchedState _forcePauseFlags;
  46. public KernelResult ObjSyncResult { get; set; }
  47. public int BasePriority { get; set; }
  48. public int PreferredCore { get; set; }
  49. public int CurrentCore { get; set; }
  50. public int ActiveCore { get; set; }
  51. private long _affinityMaskOverride;
  52. private int _preferredCoreOverride;
  53. #pragma warning disable CS0649
  54. private int _affinityOverrideCount;
  55. #pragma warning restore CS0649
  56. public ThreadSchedState SchedFlags { get; private set; }
  57. private int _shallBeTerminated;
  58. public bool ShallBeTerminated
  59. {
  60. get => _shallBeTerminated != 0;
  61. set => _shallBeTerminated = value ? 1 : 0;
  62. }
  63. public bool TerminationRequested => ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending;
  64. public bool SyncCancelled { get; set; }
  65. public bool WaitingSync { get; set; }
  66. private int _hasExited;
  67. private bool _hasBeenInitialized;
  68. private bool _hasBeenReleased;
  69. public bool WaitingInArbitration { get; set; }
  70. public long LastPc { get; set; }
  71. public KThread(KernelContext context) : base(context)
  72. {
  73. WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects];
  74. WaitSyncHandles = new int[MaxWaitSyncObjects];
  75. SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
  76. _mutexWaiters = new LinkedList<KThread>();
  77. }
  78. public KernelResult Initialize(
  79. ulong entrypoint,
  80. ulong argsPtr,
  81. ulong stackTop,
  82. int priority,
  83. int cpuCore,
  84. KProcess owner,
  85. ThreadType type,
  86. ThreadStart customThreadStart = null)
  87. {
  88. if ((uint)type > 3)
  89. {
  90. throw new ArgumentException($"Invalid thread type \"{type}\".");
  91. }
  92. ThreadContext = new KThreadContext();
  93. PreferredCore = cpuCore;
  94. AffinityMask |= 1L << cpuCore;
  95. SchedFlags = type == ThreadType.Dummy
  96. ? ThreadSchedState.Running
  97. : ThreadSchedState.None;
  98. ActiveCore = cpuCore;
  99. ObjSyncResult = KernelResult.ThreadNotStarted;
  100. DynamicPriority = priority;
  101. BasePriority = priority;
  102. CurrentCore = cpuCore;
  103. _entrypoint = entrypoint;
  104. _customThreadStart = customThreadStart;
  105. if (type == ThreadType.User)
  106. {
  107. if (owner.AllocateThreadLocalStorage(out _tlsAddress) != KernelResult.Success)
  108. {
  109. return KernelResult.OutOfMemory;
  110. }
  111. MemoryHelper.FillWithZeros(owner.CpuMemory, _tlsAddress, KTlsPageInfo.TlsEntrySize);
  112. }
  113. bool is64Bits;
  114. if (owner != null)
  115. {
  116. Owner = owner;
  117. owner.IncrementReferenceCount();
  118. owner.IncrementThreadCount();
  119. is64Bits = owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit);
  120. }
  121. else
  122. {
  123. is64Bits = true;
  124. }
  125. HostThread = new Thread(ThreadStart);
  126. Context = CpuContext.CreateExecutionContext();
  127. Context.IsAarch32 = !is64Bits;
  128. Context.SetX(0, argsPtr);
  129. if (is64Bits)
  130. {
  131. Context.SetX(31, stackTop);
  132. }
  133. else
  134. {
  135. Context.SetX(13, (uint)stackTop);
  136. }
  137. Context.CntfrqEl0 = 19200000;
  138. Context.Tpidr = (long)_tlsAddress;
  139. ThreadUid = KernelContext.NewThreadUid();
  140. HostThread.Name = customThreadStart != null ? $"HLE.OsThread.{ThreadUid}" : $"HLE.GuestThread.{ThreadUid}";
  141. _hasBeenInitialized = true;
  142. if (owner != null)
  143. {
  144. owner.SubscribeThreadEventHandlers(Context);
  145. owner.AddThread(this);
  146. if (owner.IsPaused)
  147. {
  148. KernelContext.CriticalSection.Enter();
  149. if (TerminationRequested)
  150. {
  151. KernelContext.CriticalSection.Leave();
  152. return KernelResult.Success;
  153. }
  154. _forcePauseFlags |= ThreadSchedState.ProcessPauseFlag;
  155. CombineForcePauseFlags();
  156. KernelContext.CriticalSection.Leave();
  157. }
  158. }
  159. return KernelResult.Success;
  160. }
  161. public KernelResult Start()
  162. {
  163. if (!KernelContext.KernelInitialized)
  164. {
  165. KernelContext.CriticalSection.Enter();
  166. if (!TerminationRequested)
  167. {
  168. _forcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
  169. CombineForcePauseFlags();
  170. }
  171. KernelContext.CriticalSection.Leave();
  172. }
  173. KernelResult result = KernelResult.ThreadTerminating;
  174. KernelContext.CriticalSection.Enter();
  175. if (!ShallBeTerminated)
  176. {
  177. KThread currentThread = KernelStatic.GetCurrentThread();
  178. while (SchedFlags != ThreadSchedState.TerminationPending && (currentThread == null || !currentThread.TerminationRequested))
  179. {
  180. if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
  181. {
  182. result = KernelResult.InvalidState;
  183. break;
  184. }
  185. if (currentThread == null || currentThread._forcePauseFlags == ThreadSchedState.None)
  186. {
  187. if (Owner != null && _forcePauseFlags != ThreadSchedState.None)
  188. {
  189. CombineForcePauseFlags();
  190. }
  191. SetNewSchedFlags(ThreadSchedState.Running);
  192. StartHostThread();
  193. result = KernelResult.Success;
  194. break;
  195. }
  196. else
  197. {
  198. currentThread.CombineForcePauseFlags();
  199. KernelContext.CriticalSection.Leave();
  200. KernelContext.CriticalSection.Enter();
  201. if (currentThread.ShallBeTerminated)
  202. {
  203. break;
  204. }
  205. }
  206. }
  207. }
  208. KernelContext.CriticalSection.Leave();
  209. return result;
  210. }
  211. public ThreadSchedState PrepareForTermination()
  212. {
  213. KernelContext.CriticalSection.Enter();
  214. ThreadSchedState result;
  215. if (Interlocked.CompareExchange(ref _shallBeTerminated, 1, 0) == 0)
  216. {
  217. if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.None)
  218. {
  219. SchedFlags = ThreadSchedState.TerminationPending;
  220. }
  221. else
  222. {
  223. if (_forcePauseFlags != ThreadSchedState.None)
  224. {
  225. _forcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
  226. ThreadSchedState oldSchedFlags = SchedFlags;
  227. SchedFlags &= ThreadSchedState.LowMask;
  228. AdjustScheduling(oldSchedFlags);
  229. }
  230. if (BasePriority >= 0x10)
  231. {
  232. SetPriority(0xF);
  233. }
  234. if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Running)
  235. {
  236. // TODO: GIC distributor stuffs (sgir changes ect)
  237. Context.RequestInterrupt();
  238. }
  239. SignaledObj = null;
  240. ObjSyncResult = KernelResult.ThreadTerminating;
  241. ReleaseAndResume();
  242. }
  243. }
  244. result = SchedFlags;
  245. KernelContext.CriticalSection.Leave();
  246. return result & ThreadSchedState.LowMask;
  247. }
  248. public void Terminate()
  249. {
  250. ThreadSchedState state = PrepareForTermination();
  251. if (state != ThreadSchedState.TerminationPending)
  252. {
  253. KernelContext.Synchronization.WaitFor(new KSynchronizationObject[] { this }, -1, out _);
  254. }
  255. }
  256. public void HandlePostSyscall()
  257. {
  258. ThreadSchedState state;
  259. do
  260. {
  261. if (TerminationRequested)
  262. {
  263. Exit();
  264. // As the death of the thread is handled by the CPU emulator, we differ from the official kernel and return here.
  265. break;
  266. }
  267. KernelContext.CriticalSection.Enter();
  268. if (TerminationRequested)
  269. {
  270. state = ThreadSchedState.TerminationPending;
  271. }
  272. else
  273. {
  274. if (_forcePauseFlags != ThreadSchedState.None)
  275. {
  276. CombineForcePauseFlags();
  277. }
  278. state = ThreadSchedState.Running;
  279. }
  280. KernelContext.CriticalSection.Leave();
  281. } while (state == ThreadSchedState.TerminationPending);
  282. }
  283. public void Exit()
  284. {
  285. // TODO: Debug event.
  286. if (Owner != null)
  287. {
  288. Owner.ResourceLimit?.Release(LimitableResource.Thread, 0, 1);
  289. _hasBeenReleased = true;
  290. }
  291. KernelContext.CriticalSection.Enter();
  292. _forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
  293. bool decRef = ExitImpl();
  294. Context.StopRunning();
  295. KernelContext.CriticalSection.Leave();
  296. if (decRef)
  297. {
  298. DecrementReferenceCount();
  299. }
  300. }
  301. private bool ExitImpl()
  302. {
  303. KernelContext.CriticalSection.Enter();
  304. SetNewSchedFlags(ThreadSchedState.TerminationPending);
  305. bool decRef = Interlocked.Exchange(ref _hasExited, 1) == 0;
  306. Signal();
  307. KernelContext.CriticalSection.Leave();
  308. return decRef;
  309. }
  310. public KernelResult Sleep(long timeout)
  311. {
  312. KernelContext.CriticalSection.Enter();
  313. if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
  314. {
  315. KernelContext.CriticalSection.Leave();
  316. return KernelResult.ThreadTerminating;
  317. }
  318. SetNewSchedFlags(ThreadSchedState.Paused);
  319. if (timeout > 0)
  320. {
  321. KernelContext.TimeManager.ScheduleFutureInvocation(this, timeout);
  322. }
  323. KernelContext.CriticalSection.Leave();
  324. if (timeout > 0)
  325. {
  326. KernelContext.TimeManager.UnscheduleFutureInvocation(this);
  327. }
  328. return 0;
  329. }
  330. public void SetPriority(int priority)
  331. {
  332. KernelContext.CriticalSection.Enter();
  333. BasePriority = priority;
  334. UpdatePriorityInheritance();
  335. KernelContext.CriticalSection.Leave();
  336. }
  337. public KernelResult SetActivity(bool pause)
  338. {
  339. KernelResult result = KernelResult.Success;
  340. KernelContext.CriticalSection.Enter();
  341. ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask;
  342. if (lowNibble != ThreadSchedState.Paused && lowNibble != ThreadSchedState.Running)
  343. {
  344. KernelContext.CriticalSection.Leave();
  345. return KernelResult.InvalidState;
  346. }
  347. KernelContext.CriticalSection.Enter();
  348. if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
  349. {
  350. if (pause)
  351. {
  352. // Pause, the force pause flag should be clear (thread is NOT paused).
  353. if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
  354. {
  355. _forcePauseFlags |= ThreadSchedState.ThreadPauseFlag;
  356. CombineForcePauseFlags();
  357. }
  358. else
  359. {
  360. result = KernelResult.InvalidState;
  361. }
  362. }
  363. else
  364. {
  365. // Unpause, the force pause flag should be set (thread is paused).
  366. if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
  367. {
  368. ThreadSchedState oldForcePauseFlags = _forcePauseFlags;
  369. _forcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
  370. if ((oldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None)
  371. {
  372. ThreadSchedState oldSchedFlags = SchedFlags;
  373. SchedFlags &= ThreadSchedState.LowMask;
  374. AdjustScheduling(oldSchedFlags);
  375. }
  376. }
  377. else
  378. {
  379. result = KernelResult.InvalidState;
  380. }
  381. }
  382. }
  383. KernelContext.CriticalSection.Leave();
  384. KernelContext.CriticalSection.Leave();
  385. return result;
  386. }
  387. public void CancelSynchronization()
  388. {
  389. KernelContext.CriticalSection.Enter();
  390. if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.Paused || !WaitingSync)
  391. {
  392. SyncCancelled = true;
  393. }
  394. else if (Withholder != null)
  395. {
  396. Withholder.Remove(WithholderNode);
  397. SetNewSchedFlags(ThreadSchedState.Running);
  398. Withholder = null;
  399. SyncCancelled = true;
  400. }
  401. else
  402. {
  403. SignaledObj = null;
  404. ObjSyncResult = KernelResult.Cancelled;
  405. SetNewSchedFlags(ThreadSchedState.Running);
  406. SyncCancelled = false;
  407. }
  408. KernelContext.CriticalSection.Leave();
  409. }
  410. public KernelResult SetCoreAndAffinityMask(int newCore, long newAffinityMask)
  411. {
  412. KernelContext.CriticalSection.Enter();
  413. bool useOverride = _affinityOverrideCount != 0;
  414. // The value -3 is "do not change the preferred core".
  415. if (newCore == -3)
  416. {
  417. newCore = useOverride ? _preferredCoreOverride : PreferredCore;
  418. if ((newAffinityMask & (1 << newCore)) == 0)
  419. {
  420. KernelContext.CriticalSection.Leave();
  421. return KernelResult.InvalidCombination;
  422. }
  423. }
  424. if (useOverride)
  425. {
  426. _preferredCoreOverride = newCore;
  427. _affinityMaskOverride = newAffinityMask;
  428. }
  429. else
  430. {
  431. long oldAffinityMask = AffinityMask;
  432. PreferredCore = newCore;
  433. AffinityMask = newAffinityMask;
  434. if (oldAffinityMask != newAffinityMask)
  435. {
  436. int oldCore = ActiveCore;
  437. if (oldCore >= 0 && ((AffinityMask >> oldCore) & 1) == 0)
  438. {
  439. if (PreferredCore < 0)
  440. {
  441. ActiveCore = sizeof(ulong) * 8 - 1 - BitOperations.LeadingZeroCount((ulong)AffinityMask);
  442. }
  443. else
  444. {
  445. ActiveCore = PreferredCore;
  446. }
  447. }
  448. AdjustSchedulingForNewAffinity(oldAffinityMask, oldCore);
  449. }
  450. }
  451. KernelContext.CriticalSection.Leave();
  452. return KernelResult.Success;
  453. }
  454. private void CombineForcePauseFlags()
  455. {
  456. ThreadSchedState oldFlags = SchedFlags;
  457. ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask;
  458. SchedFlags = lowNibble | _forcePauseFlags;
  459. AdjustScheduling(oldFlags);
  460. }
  461. private void SetNewSchedFlags(ThreadSchedState newFlags)
  462. {
  463. KernelContext.CriticalSection.Enter();
  464. ThreadSchedState oldFlags = SchedFlags;
  465. SchedFlags = (oldFlags & ThreadSchedState.HighMask) | newFlags;
  466. if ((oldFlags & ThreadSchedState.LowMask) != newFlags)
  467. {
  468. AdjustScheduling(oldFlags);
  469. }
  470. KernelContext.CriticalSection.Leave();
  471. }
  472. public void ReleaseAndResume()
  473. {
  474. KernelContext.CriticalSection.Enter();
  475. if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
  476. {
  477. if (Withholder != null)
  478. {
  479. Withholder.Remove(WithholderNode);
  480. SetNewSchedFlags(ThreadSchedState.Running);
  481. Withholder = null;
  482. }
  483. else
  484. {
  485. SetNewSchedFlags(ThreadSchedState.Running);
  486. }
  487. }
  488. KernelContext.CriticalSection.Leave();
  489. }
  490. public void Reschedule(ThreadSchedState newFlags)
  491. {
  492. KernelContext.CriticalSection.Enter();
  493. ThreadSchedState oldFlags = SchedFlags;
  494. SchedFlags = (oldFlags & ThreadSchedState.HighMask) |
  495. (newFlags & ThreadSchedState.LowMask);
  496. AdjustScheduling(oldFlags);
  497. KernelContext.CriticalSection.Leave();
  498. }
  499. public void AddMutexWaiter(KThread requester)
  500. {
  501. AddToMutexWaitersList(requester);
  502. requester.MutexOwner = this;
  503. UpdatePriorityInheritance();
  504. }
  505. public void RemoveMutexWaiter(KThread thread)
  506. {
  507. if (thread._mutexWaiterNode?.List != null)
  508. {
  509. _mutexWaiters.Remove(thread._mutexWaiterNode);
  510. }
  511. thread.MutexOwner = null;
  512. UpdatePriorityInheritance();
  513. }
  514. public KThread RelinquishMutex(ulong mutexAddress, out int count)
  515. {
  516. count = 0;
  517. if (_mutexWaiters.First == null)
  518. {
  519. return null;
  520. }
  521. KThread newMutexOwner = null;
  522. LinkedListNode<KThread> currentNode = _mutexWaiters.First;
  523. do
  524. {
  525. // Skip all threads that are not waiting for this mutex.
  526. while (currentNode != null && currentNode.Value.MutexAddress != mutexAddress)
  527. {
  528. currentNode = currentNode.Next;
  529. }
  530. if (currentNode == null)
  531. {
  532. break;
  533. }
  534. LinkedListNode<KThread> nextNode = currentNode.Next;
  535. _mutexWaiters.Remove(currentNode);
  536. currentNode.Value.MutexOwner = newMutexOwner;
  537. if (newMutexOwner != null)
  538. {
  539. // New owner was already selected, re-insert on new owner list.
  540. newMutexOwner.AddToMutexWaitersList(currentNode.Value);
  541. }
  542. else
  543. {
  544. // New owner not selected yet, use current thread.
  545. newMutexOwner = currentNode.Value;
  546. }
  547. count++;
  548. currentNode = nextNode;
  549. }
  550. while (currentNode != null);
  551. if (newMutexOwner != null)
  552. {
  553. UpdatePriorityInheritance();
  554. newMutexOwner.UpdatePriorityInheritance();
  555. }
  556. return newMutexOwner;
  557. }
  558. private void UpdatePriorityInheritance()
  559. {
  560. // If any of the threads waiting for the mutex has
  561. // higher priority than the current thread, then
  562. // the current thread inherits that priority.
  563. int highestPriority = BasePriority;
  564. if (_mutexWaiters.First != null)
  565. {
  566. int waitingDynamicPriority = _mutexWaiters.First.Value.DynamicPriority;
  567. if (waitingDynamicPriority < highestPriority)
  568. {
  569. highestPriority = waitingDynamicPriority;
  570. }
  571. }
  572. if (highestPriority != DynamicPriority)
  573. {
  574. int oldPriority = DynamicPriority;
  575. DynamicPriority = highestPriority;
  576. AdjustSchedulingForNewPriority(oldPriority);
  577. if (MutexOwner != null)
  578. {
  579. // Remove and re-insert to ensure proper sorting based on new priority.
  580. MutexOwner._mutexWaiters.Remove(_mutexWaiterNode);
  581. MutexOwner.AddToMutexWaitersList(this);
  582. MutexOwner.UpdatePriorityInheritance();
  583. }
  584. }
  585. }
  586. private void AddToMutexWaitersList(KThread thread)
  587. {
  588. LinkedListNode<KThread> nextPrio = _mutexWaiters.First;
  589. int currentPriority = thread.DynamicPriority;
  590. while (nextPrio != null && nextPrio.Value.DynamicPriority <= currentPriority)
  591. {
  592. nextPrio = nextPrio.Next;
  593. }
  594. if (nextPrio != null)
  595. {
  596. thread._mutexWaiterNode = _mutexWaiters.AddBefore(nextPrio, thread);
  597. }
  598. else
  599. {
  600. thread._mutexWaiterNode = _mutexWaiters.AddLast(thread);
  601. }
  602. }
  603. private void AdjustScheduling(ThreadSchedState oldFlags)
  604. {
  605. if (oldFlags == SchedFlags)
  606. {
  607. return;
  608. }
  609. if (!IsSchedulable)
  610. {
  611. // Ensure our thread is running and we have an event.
  612. StartHostThread();
  613. // If the thread is not schedulable, we want to just run or pause
  614. // it directly as we don't care about priority or the core it is
  615. // running on in this case.
  616. if (SchedFlags == ThreadSchedState.Running)
  617. {
  618. _schedulerWaitEvent.Set();
  619. }
  620. else
  621. {
  622. _schedulerWaitEvent.Reset();
  623. }
  624. return;
  625. }
  626. if (oldFlags == ThreadSchedState.Running)
  627. {
  628. // Was running, now it's stopped.
  629. if (ActiveCore >= 0)
  630. {
  631. KernelContext.PriorityQueue.Unschedule(DynamicPriority, ActiveCore, this);
  632. }
  633. for (int core = 0; core < KScheduler.CpuCoresCount; core++)
  634. {
  635. if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0)
  636. {
  637. KernelContext.PriorityQueue.Unsuggest(DynamicPriority, core, this);
  638. }
  639. }
  640. }
  641. else if (SchedFlags == ThreadSchedState.Running)
  642. {
  643. // Was stopped, now it's running.
  644. if (ActiveCore >= 0)
  645. {
  646. KernelContext.PriorityQueue.Schedule(DynamicPriority, ActiveCore, this);
  647. }
  648. for (int core = 0; core < KScheduler.CpuCoresCount; core++)
  649. {
  650. if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0)
  651. {
  652. KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this);
  653. }
  654. }
  655. }
  656. KernelContext.ThreadReselectionRequested = true;
  657. }
  658. private void AdjustSchedulingForNewPriority(int oldPriority)
  659. {
  660. if (SchedFlags != ThreadSchedState.Running || !IsSchedulable)
  661. {
  662. return;
  663. }
  664. // Remove thread from the old priority queues.
  665. if (ActiveCore >= 0)
  666. {
  667. KernelContext.PriorityQueue.Unschedule(oldPriority, ActiveCore, this);
  668. }
  669. for (int core = 0; core < KScheduler.CpuCoresCount; core++)
  670. {
  671. if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0)
  672. {
  673. KernelContext.PriorityQueue.Unsuggest(oldPriority, core, this);
  674. }
  675. }
  676. // Add thread to the new priority queues.
  677. KThread currentThread = KernelStatic.GetCurrentThread();
  678. if (ActiveCore >= 0)
  679. {
  680. if (currentThread == this)
  681. {
  682. KernelContext.PriorityQueue.SchedulePrepend(DynamicPriority, ActiveCore, this);
  683. }
  684. else
  685. {
  686. KernelContext.PriorityQueue.Schedule(DynamicPriority, ActiveCore, this);
  687. }
  688. }
  689. for (int core = 0; core < KScheduler.CpuCoresCount; core++)
  690. {
  691. if (core != ActiveCore && ((AffinityMask >> core) & 1) != 0)
  692. {
  693. KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this);
  694. }
  695. }
  696. KernelContext.ThreadReselectionRequested = true;
  697. }
  698. private void AdjustSchedulingForNewAffinity(long oldAffinityMask, int oldCore)
  699. {
  700. if (SchedFlags != ThreadSchedState.Running || DynamicPriority >= KScheduler.PrioritiesCount || !IsSchedulable)
  701. {
  702. return;
  703. }
  704. // Remove thread from the old priority queues.
  705. for (int core = 0; core < KScheduler.CpuCoresCount; core++)
  706. {
  707. if (((oldAffinityMask >> core) & 1) != 0)
  708. {
  709. if (core == oldCore)
  710. {
  711. KernelContext.PriorityQueue.Unschedule(DynamicPriority, core, this);
  712. }
  713. else
  714. {
  715. KernelContext.PriorityQueue.Unsuggest(DynamicPriority, core, this);
  716. }
  717. }
  718. }
  719. // Add thread to the new priority queues.
  720. for (int core = 0; core < KScheduler.CpuCoresCount; core++)
  721. {
  722. if (((AffinityMask >> core) & 1) != 0)
  723. {
  724. if (core == ActiveCore)
  725. {
  726. KernelContext.PriorityQueue.Schedule(DynamicPriority, core, this);
  727. }
  728. else
  729. {
  730. KernelContext.PriorityQueue.Suggest(DynamicPriority, core, this);
  731. }
  732. }
  733. }
  734. KernelContext.ThreadReselectionRequested = true;
  735. }
  736. public void SetEntryArguments(long argsPtr, int threadHandle)
  737. {
  738. Context.SetX(0, (ulong)argsPtr);
  739. Context.SetX(1, (ulong)threadHandle);
  740. }
  741. public void TimeUp()
  742. {
  743. ReleaseAndResume();
  744. }
  745. public string GetGuestStackTrace()
  746. {
  747. return Owner.Debugger.GetGuestStackTrace(this);
  748. }
  749. public string GetGuestRegisterPrintout()
  750. {
  751. return Owner.Debugger.GetCpuRegisterPrintout(this);
  752. }
  753. public void PrintGuestStackTrace()
  754. {
  755. Logger.Info?.Print(LogClass.Cpu, $"Guest stack trace:\n{GetGuestStackTrace()}\n");
  756. }
  757. public void PrintGuestRegisterPrintout()
  758. {
  759. Logger.Info?.Print(LogClass.Cpu, $"Guest CPU registers:\n{GetGuestRegisterPrintout()}\n");
  760. }
  761. public void AddCpuTime(long ticks)
  762. {
  763. Interlocked.Add(ref _totalTimeRunning, ticks);
  764. }
  765. public void StartHostThread()
  766. {
  767. if (_schedulerWaitEvent == null)
  768. {
  769. var schedulerWaitEvent = new ManualResetEvent(false);
  770. if (Interlocked.Exchange(ref _schedulerWaitEvent, schedulerWaitEvent) == null)
  771. {
  772. HostThread.Start();
  773. }
  774. else
  775. {
  776. schedulerWaitEvent.Dispose();
  777. }
  778. }
  779. }
  780. private void ThreadStart()
  781. {
  782. _schedulerWaitEvent.WaitOne();
  783. KernelStatic.SetKernelContext(KernelContext, this);
  784. if (_customThreadStart != null)
  785. {
  786. _customThreadStart();
  787. }
  788. else
  789. {
  790. Owner.Context.Execute(Context, _entrypoint);
  791. }
  792. Context.Dispose();
  793. _schedulerWaitEvent.Dispose();
  794. }
  795. public void MakeUnschedulable()
  796. {
  797. _forcedUnschedulable = true;
  798. }
  799. public override bool IsSignaled()
  800. {
  801. return _hasExited != 0;
  802. }
  803. protected override void Destroy()
  804. {
  805. if (_hasBeenInitialized)
  806. {
  807. FreeResources();
  808. bool released = Owner != null || _hasBeenReleased;
  809. if (Owner != null)
  810. {
  811. Owner.ResourceLimit?.Release(LimitableResource.Thread, 1, released ? 0 : 1);
  812. Owner.DecrementReferenceCount();
  813. }
  814. else
  815. {
  816. KernelContext.ResourceLimit.Release(LimitableResource.Thread, 1, released ? 0 : 1);
  817. }
  818. }
  819. }
  820. private void FreeResources()
  821. {
  822. Owner?.RemoveThread(this);
  823. if (_tlsAddress != 0 && Owner.FreeThreadLocalStorage(_tlsAddress) != KernelResult.Success)
  824. {
  825. throw new InvalidOperationException("Unexpected failure freeing thread local storage.");
  826. }
  827. KernelContext.CriticalSection.Enter();
  828. // Wake up all threads that may be waiting for a mutex being held by this thread.
  829. foreach (KThread thread in _mutexWaiters)
  830. {
  831. thread.MutexOwner = null;
  832. thread._preferredCoreOverride = 0;
  833. thread.ObjSyncResult = KernelResult.InvalidState;
  834. thread.ReleaseAndResume();
  835. }
  836. KernelContext.CriticalSection.Leave();
  837. Owner?.DecrementThreadCountAndTerminateIfZero();
  838. }
  839. }
  840. }