4719dc9d97dd1d583fab81508d7de69e768b9889
[platform/upstream/coreclr.git] / src / gc / unix / gcenv.unix.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 #include <cstdint>
6 #include <cstddef>
7 #include <cassert>
8 #include <memory>
9 #include <pthread.h>
10 #include <signal.h>
11
12 #include "config.h"
13 #include "common.h"
14
15 #include "gcenv.structs.h"
16 #include "gcenv.base.h"
17 #include "gcenv.os.h"
18 #include "gcenv.unix.inl"
19 #include "volatile.h"
20
21 #if HAVE_SYS_TIME_H
22  #include <sys/time.h>
23 #else
24  #error "sys/time.h required by GC PAL for the time being"
25 #endif // HAVE_SYS_TIME_
26
27 #if HAVE_SYS_MMAN_H
28  #include <sys/mman.h>
29 #else
30  #error "sys/mman.h required by GC PAL"
31 #endif // HAVE_SYS_MMAN_H
32
33 #ifdef __linux__
34  #include <sys/syscall.h>
35 #endif // __linux__
36
37 #include <time.h> // nanosleep
38 #include <sched.h> // sched_yield
39 #include <errno.h>
40 #include <unistd.h> // sysconf
41 #include "globals.h"
42 #include "cgroup.h"
43
44 #if defined(_ARM_) || defined(_ARM64_)
45 #define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_CONF
46 #else
47 #define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_ONLN
48 #endif
49
50 // The cached number of logical CPUs observed.
51 static uint32_t g_logicalCpuCount = 0;
52
53 // The cached number of CPUs available for the current process.
54 static uint32_t g_currentProcessCpuCount = 0;
55
56 // Helper memory page used by the FlushProcessWriteBuffers
57 static uint8_t* g_helperPage = 0;
58
59 // Mutex to make the FlushProcessWriteBuffersMutex thread safe
60 static pthread_mutex_t g_flushProcessWriteBuffersMutex;
61
62 size_t GetRestrictedPhysicalMemoryLimit();
63 bool GetPhysicalMemoryUsed(size_t* val);
64 bool GetCpuLimit(uint32_t* val);
65
66 static size_t g_RestrictedPhysicalMemoryLimit = 0;
67
68 uint32_t g_pageSizeUnixInl = 0;
69
70 AffinitySet g_processAffinitySet;
71
72 // Initialize the interface implementation
73 // Return:
74 //  true if it has succeeded, false if it has failed
75 bool GCToOSInterface::Initialize()
76 {
77     int pageSize = sysconf( _SC_PAGE_SIZE );
78
79     g_pageSizeUnixInl = uint32_t((pageSize > 0) ? pageSize : 0x1000);
80
81     // Calculate and cache the number of processors on this machine
82     int cpuCount = sysconf(SYSCONF_GET_NUMPROCS);
83     if (cpuCount == -1)
84     {
85         return false;
86     }
87
88     g_logicalCpuCount = cpuCount;
89
90     assert(g_helperPage == 0);
91
92     g_helperPage = static_cast<uint8_t*>(mmap(0, OS_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
93
94     if(g_helperPage == MAP_FAILED)
95     {
96         return false;
97     }
98
99     // Verify that the s_helperPage is really aligned to the g_SystemInfo.dwPageSize
100     assert((((size_t)g_helperPage) & (OS_PAGE_SIZE - 1)) == 0);
101
102     // Locking the page ensures that it stays in memory during the two mprotect
103     // calls in the FlushProcessWriteBuffers below. If the page was unmapped between
104     // those calls, they would not have the expected effect of generating IPI.
105     int status = mlock(g_helperPage, OS_PAGE_SIZE);
106
107     if (status != 0)
108     {
109         return false;
110     }
111
112     status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL);
113     if (status != 0)
114     {
115         munlock(g_helperPage, OS_PAGE_SIZE);
116         return false;
117     }
118
119 #if HAVE_MACH_ABSOLUTE_TIME
120     kern_return_t machRet;
121     if ((machRet = mach_timebase_info(&g_TimebaseInfo)) != KERN_SUCCESS)
122     {
123         return false;
124     }
125 #endif // HAVE_MACH_ABSOLUTE_TIME
126
127     InitializeCGroup();
128
129 #if HAVE_SCHED_GETAFFINITY
130
131     g_currentProcessCpuCount = 0;
132
133     cpu_set_t cpuSet;
134     int st = sched_getaffinity(0, sizeof(cpu_set_t), &cpuSet);
135
136     if (st == 0)
137     {
138         for (size_t i = 0; i < g_logicalCpuCount; i++)
139         {
140             if (CPU_ISSET(i, &cpuSet))
141             {
142                 g_currentProcessCpuCount++;
143                 g_processAffinitySet.Add(i);
144             }
145         }
146     }
147     else
148     {
149         // We should not get any of the errors that the sched_getaffinity can return since none
150         // of them applies for the current thread, so this is an unexpected kind of failure.
151         assert(false);
152     }
153
154 #else // HAVE_SCHED_GETAFFINITY
155
156     g_currentProcessCpuCount = g_logicalCpuCount;
157
158     for (size_t i = 0; i < g_logicalCpuCount; i++)
159     {
160         g_processAffinitySet.Add(i);
161     }
162
163 #endif // HAVE_SCHED_GETAFFINITY
164
165     return true;
166 }
167
168 // Shutdown the interface implementation
169 void GCToOSInterface::Shutdown()
170 {
171     int ret = munlock(g_helperPage, OS_PAGE_SIZE);
172     assert(ret == 0);
173     ret = pthread_mutex_destroy(&g_flushProcessWriteBuffersMutex);
174     assert(ret == 0);
175
176     munmap(g_helperPage, OS_PAGE_SIZE);
177
178     CleanupCGroup();
179 }
180
181 // Get numeric id of the current thread if possible on the
182 // current platform. It is indended for logging purposes only.
183 // Return:
184 //  Numeric id of the current thread, as best we can retrieve it.
185 uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
186 {
187 #if defined(__linux__)
188     return (uint64_t)syscall(SYS_gettid);
189 #elif HAVE_PTHREAD_GETTHREADID_NP
190     return (uint64_t)pthread_getthreadid_np();
191 #elif HAVE_PTHREAD_THREADID_NP
192     unsigned long long tid;
193     pthread_threadid_np(pthread_self(), &tid);
194     return (uint64_t)tid;
195 #else
196     // Fallback in case we don't know how to get integer thread id on the current platform
197     return (uint64_t)pthread_self();
198 #endif
199 }
200
201 // Get the process ID of the process.
202 uint32_t GCToOSInterface::GetCurrentProcessId()
203 {
204     return getpid();
205 }
206
207 // Set ideal processor for the current thread
208 // Parameters:
209 //  srcProcNo - processor number the thread currently runs on
210 //  dstProcNo - processor number the thread should be migrated to
211 // Return:
212 //  true if it has succeeded, false if it has failed
213 bool GCToOSInterface::SetCurrentThreadIdealAffinity(uint16_t srcProcNo, uint16_t dstProcNo)
214 {
215     return GCToOSInterface::SetThreadAffinity(dstProcNo);
216 }
217
218 // Get the number of the current processor
219 uint32_t GCToOSInterface::GetCurrentProcessorNumber()
220 {
221 #if HAVE_SCHED_GETCPU
222     int processorNumber = sched_getcpu();
223     assert(processorNumber != -1);
224     return processorNumber;
225 #else
226     return 0;
227 #endif
228 }
229
230 // Check if the OS supports getting current processor number
231 bool GCToOSInterface::CanGetCurrentProcessorNumber()
232 {
233     return HAVE_SCHED_GETCPU;
234 }
235
236 // Flush write buffers of processors that are executing threads of the current process
237 void GCToOSInterface::FlushProcessWriteBuffers()
238 {
239     int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex);
240     assert(status == 0 && "Failed to lock the flushProcessWriteBuffersMutex lock");
241
242     // Changing a helper memory page protection from read / write to no access
243     // causes the OS to issue IPI to flush TLBs on all processors. This also
244     // results in flushing the processor buffers.
245     status = mprotect(g_helperPage, OS_PAGE_SIZE, PROT_READ | PROT_WRITE);
246     assert(status == 0 && "Failed to change helper page protection to read / write");
247
248     // Ensure that the page is dirty before we change the protection so that
249     // we prevent the OS from skipping the global TLB flush.
250     __sync_add_and_fetch((size_t*)g_helperPage, 1);
251
252     status = mprotect(g_helperPage, OS_PAGE_SIZE, PROT_NONE);
253     assert(status == 0 && "Failed to change helper page protection to no access");
254
255     status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex);
256     assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock");
257 }
258
259 // Break into a debugger. Uses a compiler intrinsic if one is available,
260 // otherwise raises a SIGTRAP.
261 void GCToOSInterface::DebugBreak()
262 {
263     // __has_builtin is only defined by clang. GCC doesn't have a debug
264     // trap intrinsic anyway.
265 #ifndef __has_builtin
266  #define __has_builtin(x) 0
267 #endif // __has_builtin
268
269 #if __has_builtin(__builtin_debugtrap)
270     __builtin_debugtrap();
271 #else
272     raise(SIGTRAP);
273 #endif
274 }
275
276 // Causes the calling thread to sleep for the specified number of milliseconds
277 // Parameters:
278 //  sleepMSec   - time to sleep before switching to another thread
279 void GCToOSInterface::Sleep(uint32_t sleepMSec)
280 {
281     if (sleepMSec == 0)
282     {
283         return;
284     }
285
286     timespec requested;
287     requested.tv_sec = sleepMSec / tccSecondsToMilliSeconds;
288     requested.tv_nsec = (sleepMSec - requested.tv_sec * tccSecondsToMilliSeconds) * tccMilliSecondsToNanoSeconds;
289
290     timespec remaining;
291     while (nanosleep(&requested, &remaining) == EINTR)
292     {
293         requested = remaining;
294     }
295 }
296
297 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
298 // Parameters:
299 //  switchCount - number of times the YieldThread was called in a loop
300 void GCToOSInterface::YieldThread(uint32_t switchCount)
301 {
302     int ret = sched_yield();
303
304     // sched_yield never fails on Linux, unclear about other OSes
305     assert(ret == 0);
306 }
307
308 // Reserve virtual memory range.
309 // Parameters:
310 //  size      - size of the virtual memory range
311 //  alignment - requested memory alignment, 0 means no specific alignment requested
312 //  flags     - flags to control special settings like write watching
313 // Return:
314 //  Starting virtual address of the reserved range
315 void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
316 {
317     assert(!(flags & VirtualReserveFlags::WriteWatch) && "WriteWatch not supported on Unix");
318     if (alignment == 0)
319     {
320         alignment = OS_PAGE_SIZE;
321     }
322
323     size_t alignedSize = size + (alignment - OS_PAGE_SIZE);
324     void * pRetVal = mmap(nullptr, alignedSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
325
326     if (pRetVal != NULL)
327     {
328         void * pAlignedRetVal = (void *)(((size_t)pRetVal + (alignment - 1)) & ~(alignment - 1));
329         size_t startPadding = (size_t)pAlignedRetVal - (size_t)pRetVal;
330         if (startPadding != 0)
331         {
332             int ret = munmap(pRetVal, startPadding);
333             assert(ret == 0);
334         }
335
336         size_t endPadding = alignedSize - (startPadding + size);
337         if (endPadding != 0)
338         {
339             int ret = munmap((void *)((size_t)pAlignedRetVal + size), endPadding);
340             assert(ret == 0);
341         }
342
343         pRetVal = pAlignedRetVal;
344     }
345
346     return pRetVal;
347 }
348
349 // Release virtual memory range previously reserved using VirtualReserve
350 // Parameters:
351 //  address - starting virtual address
352 //  size    - size of the virtual memory range
353 // Return:
354 //  true if it has succeeded, false if it has failed
355 bool GCToOSInterface::VirtualRelease(void* address, size_t size)
356 {
357     int ret = munmap(address, size);
358
359     return (ret == 0);
360 }
361
362 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
363 // Parameters:
364 //  address - starting virtual address
365 //  size    - size of the virtual memory range
366 // Return:
367 //  true if it has succeeded, false if it has failed
368 bool GCToOSInterface::VirtualCommit(void* address, size_t size, uint16_t node)
369 {
370     assert(node == NUMA_NODE_UNDEFINED && "Numa allocation is not ported to local GC on unix yet");
371     return mprotect(address, size, PROT_WRITE | PROT_READ) == 0;
372 }
373
374 // Decomit virtual memory range.
375 // Parameters:
376 //  address - starting virtual address
377 //  size    - size of the virtual memory range
378 // Return:
379 //  true if it has succeeded, false if it has failed
380 bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
381 {
382     // TODO: This can fail, however the GC does not handle the failure gracefully
383     // Explicitly calling mmap instead of mprotect here makes it
384     // that much more clear to the operating system that we no
385     // longer need these pages. Also, GC depends on re-commited pages to
386     // be zeroed-out.
387     return mmap(address, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0) != NULL;
388 }
389
390 // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
391 // longer of interest, but it should not be decommitted.
392 // Parameters:
393 //  address - starting virtual address
394 //  size    - size of the virtual memory range
395 //  unlock  - true if the memory range should also be unlocked
396 // Return:
397 //  true if it has succeeded, false if it has failed
398 bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
399 {
400     int st;
401 #if HAVE_MADV_FREE
402     // Try to use MADV_FREE if supported. It tells the kernel that the application doesn't
403     // need the pages in the range. Freeing the pages can be delayed until a memory pressure
404     // occurs.
405     st = madvise(address, size, MADV_FREE);
406     if (st != 0)
407 #endif    
408     {
409         // In case the MADV_FREE is not supported, use MADV_DONTNEED
410         st = madvise(address, size, MADV_DONTNEED);
411     }
412
413     return (st == 0);
414 }
415
416 // Check if the OS supports write watching
417 bool GCToOSInterface::SupportsWriteWatch()
418 {
419     return false;
420 }
421
422 // Reset the write tracking state for the specified virtual memory range.
423 // Parameters:
424 //  address - starting virtual address
425 //  size    - size of the virtual memory range
426 void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
427 {
428     assert(!"should never call ResetWriteWatch on Unix");
429 }
430
431 // Retrieve addresses of the pages that are written to in a region of virtual memory
432 // Parameters:
433 //  resetState         - true indicates to reset the write tracking state
434 //  address            - starting virtual address
435 //  size               - size of the virtual memory range
436 //  pageAddresses      - buffer that receives an array of page addresses in the memory region
437 //  pageAddressesCount - on input, size of the lpAddresses array, in array elements
438 //                       on output, the number of page addresses that are returned in the array.
439 // Return:
440 //  true if it has succeeded, false if it has failed
441 bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
442 {
443     assert(!"should never call GetWriteWatch on Unix");
444     return false;
445 }
446
447 // Get size of the largest cache on the processor die
448 // Parameters:
449 //  trueSize - true to return true cache size, false to return scaled up size based on
450 //             the processor architecture
451 // Return:
452 //  Size of the cache
453 size_t GCToOSInterface::GetCacheSizePerLogicalCpu(bool trueSize)
454 {
455     // TODO(segilles) processor detection
456     return 0;
457 }
458
459 // Sets the calling thread's affinity to only run on the processor specified
460 // Parameters:
461 //  procNo - The requested processor for the calling thread.
462 // Return:
463 //  true if setting the affinity was successful, false otherwise.
464 bool GCToOSInterface::SetThreadAffinity(uint16_t procNo)
465 {
466 #if HAVE_PTHREAD_GETAFFINITY_NP
467     cpu_set_t cpuSet;
468     CPU_ZERO(&cpuSet);
469     CPU_SET((int)procNo, &cpuSet);
470
471     int st = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuSet);
472
473     return (st == 0);
474
475 #else  // HAVE_PTHREAD_GETAFFINITY_NP
476     // There is no API to manage thread affinity, so let's ignore the request
477     return false;
478 #endif // HAVE_PTHREAD_GETAFFINITY_NP
479 }
480
481 // Boosts the calling thread's thread priority to a level higher than the default
482 // for new threads.
483 // Parameters:
484 //  None.
485 // Return:
486 //  true if the priority boost was successful, false otherwise.
487 bool GCToOSInterface::BoostThreadPriority()
488 {
489     // [LOCALGC TODO] Thread priority for unix
490     return false;
491 }
492
493 // Set the set of processors enabled for GC threads for the current process based on config specified affinity mask and set
494 // Parameters:
495 //  configAffinityMask - mask specified by the GCHeapAffinitizeMask config
496 //  configAffinitySet  - affinity set specified by the GCHeapAffinitizeRanges config
497 // Return:
498 //  set of enabled processors
499 const AffinitySet* GCToOSInterface::SetGCThreadsAffinitySet(uintptr_t configAffinityMask, const AffinitySet* configAffinitySet)
500 {
501     if (!configAffinitySet->IsEmpty())
502     {
503         // Update the process affinity set using the configured set
504         for (size_t i = 0; i < MAX_SUPPORTED_CPUS; i++)
505         {
506             if (g_processAffinitySet.Contains(i) && !configAffinitySet->Contains(i))
507             {
508                 g_processAffinitySet.Remove(i);
509             }
510         }
511     }
512
513     return &g_processAffinitySet;
514 }
515
516 // Get number of processors assigned to the current process
517 // Return:
518 //  The number of processors
519 uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
520 {
521     return g_currentProcessCpuCount;
522 }
523
524 // Return the size of the user-mode portion of the virtual address space of this process.
525 // Return:
526 //  non zero if it has succeeded, 0 if it has failed
527 size_t GCToOSInterface::GetVirtualMemoryLimit()
528 {
529 #ifdef BIT64
530     // There is no API to get the total virtual address space size on
531     // Unix, so we use a constant value representing 128TB, which is
532     // the approximate size of total user virtual address space on
533     // the currently supported Unix systems.
534     static const uint64_t _128TB = (1ull << 47);
535     return _128TB;
536 #else
537     return (size_t)-1;
538 #endif
539 }
540
541 // Get the physical memory that this process can use.
542 // Return:
543 //  non zero if it has succeeded, 0 if it has failed
544 // Remarks:
545 //  If a process runs with a restricted memory limit, it returns the limit. If there's no limit 
546 //  specified, it returns amount of actual physical memory.
547 uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
548 {
549     size_t restricted_limit;
550     if (is_restricted)
551         *is_restricted = false;
552
553     // The limit was not cached
554     if (g_RestrictedPhysicalMemoryLimit == 0)
555     {
556         restricted_limit = GetRestrictedPhysicalMemoryLimit();
557         VolatileStore(&g_RestrictedPhysicalMemoryLimit, restricted_limit);
558     }
559     restricted_limit = g_RestrictedPhysicalMemoryLimit;
560
561     if (restricted_limit != 0 && restricted_limit != SIZE_T_MAX)
562     {
563         if (is_restricted)
564             *is_restricted = true;
565         return restricted_limit;
566     }
567
568     long pages = sysconf(_SC_PHYS_PAGES);
569     if (pages == -1) 
570     {
571         return 0;
572     }
573
574     long pageSize = sysconf(_SC_PAGE_SIZE);
575     if (pageSize == -1)
576     {
577         return 0;
578     }
579
580     return pages * pageSize;
581 }
582
583 // Get memory status
584 // Parameters:
585 //  memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
586 //      that is in use (0 indicates no memory use and 100 indicates full memory use).
587 //  available_physical - The amount of physical memory currently available, in bytes.
588 //  available_page_file - The maximum amount of memory the current process can commit, in bytes.
589 void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
590 {
591     if (memory_load != nullptr || available_physical != nullptr)
592     {
593         uint64_t total = GetPhysicalMemoryLimit();
594
595         uint64_t available = 0;
596         uint32_t load = 0;
597         size_t used;
598
599         // Get the physical memory in use - from it, we can get the physical memory available.
600         // We do this only when we have the total physical memory available.
601         if (total > 0 && GetPhysicalMemoryUsed(&used))
602         {
603             available = total > used ? total-used : 0; 
604             load = (uint32_t)(((float)used * 100) / (float)total);
605         }
606
607         if (memory_load != nullptr)
608             *memory_load = load;
609         if (available_physical != nullptr)
610             *available_physical = available;
611     }
612
613     if (available_page_file != nullptr)
614         *available_page_file = 0;
615 }
616
617 // Get a high precision performance counter
618 // Return:
619 //  The counter value
620 int64_t GCToOSInterface::QueryPerformanceCounter()
621 {
622     // TODO: This is not a particularly efficient implementation - we certainly could
623     // do much more specific platform-dependent versions if we find that this method
624     // runs hot. However, most likely it does not.
625     struct timeval tv;
626     if (gettimeofday(&tv, NULL) == -1)
627     {
628         assert(!"gettimeofday() failed");
629         // TODO (segilles) unconditional asserts
630         return 0;
631     }
632     return (int64_t) tv.tv_sec * (int64_t) tccSecondsToMicroSeconds + (int64_t) tv.tv_usec;
633 }
634
635 // Get a frequency of the high precision performance counter
636 // Return:
637 //  The counter frequency
638 int64_t GCToOSInterface::QueryPerformanceFrequency()
639 {
640     // The counter frequency of gettimeofday is in microseconds.
641     return tccSecondsToMicroSeconds;
642 }
643
644 // Get a time stamp with a low precision
645 // Return:
646 //  Time stamp in milliseconds
647 uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
648 {
649     // TODO(segilles) this is pretty naive, we can do better
650     uint64_t retval = 0;
651     struct timeval tv;
652     if (gettimeofday(&tv, NULL) == 0)
653     {
654         retval = (tv.tv_sec * tccSecondsToMilliSeconds) + (tv.tv_usec / tccMilliSecondsToMicroSeconds);
655     }
656     else
657     {
658         assert(!"gettimeofday() failed\n");
659     }
660
661     return retval;
662 }
663
664 // Gets the total number of processors on the machine, not taking
665 // into account current process affinity.
666 // Return:
667 //  Number of processors on the machine
668 uint32_t GCToOSInterface::GetTotalProcessorCount()
669 {
670     // Calculated in GCToOSInterface::Initialize using
671     // sysconf(_SC_NPROCESSORS_ONLN)
672     return g_logicalCpuCount;
673 }
674
675 bool GCToOSInterface::CanEnableGCNumaAware()
676 {
677     return false;
678 }
679
680 bool GCToOSInterface::GetNumaProcessorNode(uint16_t proc_no, uint16_t *node_no)
681 {
682     assert(!"Numa has not been ported to local GC for unix");
683     return false;
684 }
685
686 // Get processor number and optionally its NUMA node number for the specified heap number
687 // Parameters:
688 //  heap_number - heap number to get the result for
689 //  proc_no     - set to the selected processor number
690 //  node_no     - set to the NUMA node of the selected processor or to NUMA_NODE_UNDEFINED
691 // Return:
692 //  true if it succeeded
693 bool GCToOSInterface::GetProcessorForHeap(uint16_t heap_number, uint16_t* proc_no, uint16_t* node_no)
694 {
695     bool success = false;
696
697     uint16_t availableProcNumber = 0;
698     for (size_t procNumber = 0; procNumber < g_logicalCpuCount; procNumber++)
699     {
700         if (g_processAffinitySet.Contains(procNumber))
701         {
702             if (availableProcNumber == heap_number)
703             {
704                 *proc_no = procNumber;
705
706                 if (GCToOSInterface::CanEnableGCNumaAware())
707                 {
708                     if (!GCToOSInterface::GetNumaProcessorNode(procNumber, node_no))
709                     {
710                         *node_no = NUMA_NODE_UNDEFINED;
711                     }
712                 }
713                 else
714                 {
715                     *node_no = NUMA_NODE_UNDEFINED;
716                 }
717
718                 success = true;
719                 break;
720             }
721             availableProcNumber++;
722         }
723     }
724
725     return success;
726 }
727
728 // Initialize the critical section
729 void CLRCriticalSection::Initialize()
730 {
731     int st = pthread_mutex_init(&m_cs.mutex, NULL);
732     assert(st == 0);
733 }
734
735 // Destroy the critical section
736 void CLRCriticalSection::Destroy()
737 {
738     int st = pthread_mutex_destroy(&m_cs.mutex);
739     assert(st == 0);
740 }
741
742 // Enter the critical section. Blocks until the section can be entered.
743 void CLRCriticalSection::Enter()
744 {
745     pthread_mutex_lock(&m_cs.mutex);
746 }
747
748 // Leave the critical section
749 void CLRCriticalSection::Leave()
750 {
751     pthread_mutex_unlock(&m_cs.mutex);
752 }