Home | History | Annotate | Download | only in runtime
      1 // Copyright 2010 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package runtime
      6 
      7 import (
      8 	"unsafe"
      9 )
     10 
     11 const (
     12 	_MEM_COMMIT   = 0x1000
     13 	_MEM_RESERVE  = 0x2000
     14 	_MEM_DECOMMIT = 0x4000
     15 	_MEM_RELEASE  = 0x8000
     16 
     17 	_PAGE_READWRITE = 0x0004
     18 	_PAGE_NOACCESS  = 0x0001
     19 )
     20 
     21 // Don't split the stack as this function may be invoked without a valid G,
     22 // which prevents us from allocating more stack.
     23 //go:nosplit
     24 func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer {
     25 	mSysStatInc(sysStat, n)
     26 	return unsafe.Pointer(stdcall4(_VirtualAlloc, 0, n, _MEM_COMMIT|_MEM_RESERVE, _PAGE_READWRITE))
     27 }
     28 
     29 func sysUnused(v unsafe.Pointer, n uintptr) {
     30 	r := stdcall3(_VirtualFree, uintptr(v), n, _MEM_DECOMMIT)
     31 	if r != 0 {
     32 		return
     33 	}
     34 
     35 	// Decommit failed. Usual reason is that we've merged memory from two different
     36 	// VirtualAlloc calls, and Windows will only let each VirtualFree handle pages from
     37 	// a single VirtualAlloc. It is okay to specify a subset of the pages from a single alloc,
     38 	// just not pages from multiple allocs. This is a rare case, arising only when we're
     39 	// trying to give memory back to the operating system, which happens on a time
     40 	// scale of minutes. It doesn't have to be terribly fast. Instead of extra bookkeeping
     41 	// on all our VirtualAlloc calls, try freeing successively smaller pieces until
     42 	// we manage to free something, and then repeat. This ends up being O(n log n)
     43 	// in the worst case, but that's fast enough.
     44 	for n > 0 {
     45 		small := n
     46 		for small >= 4096 && stdcall3(_VirtualFree, uintptr(v), small, _MEM_DECOMMIT) == 0 {
     47 			small /= 2
     48 			small &^= 4096 - 1
     49 		}
     50 		if small < 4096 {
     51 			print("runtime: VirtualFree of ", small, " bytes failed with errno=", getlasterror(), "\n")
     52 			throw("runtime: failed to decommit pages")
     53 		}
     54 		v = add(v, small)
     55 		n -= small
     56 	}
     57 }
     58 
     59 func sysUsed(v unsafe.Pointer, n uintptr) {
     60 	r := stdcall4(_VirtualAlloc, uintptr(v), n, _MEM_COMMIT, _PAGE_READWRITE)
     61 	if r == uintptr(v) {
     62 		return
     63 	}
     64 
     65 	// Commit failed. See SysUnused.
     66 	for n > 0 {
     67 		small := n
     68 		for small >= 4096 && stdcall4(_VirtualAlloc, uintptr(v), small, _MEM_COMMIT, _PAGE_READWRITE) == 0 {
     69 			small /= 2
     70 			small &^= 4096 - 1
     71 		}
     72 		if small < 4096 {
     73 			print("runtime: VirtualAlloc of ", small, " bytes failed with errno=", getlasterror(), "\n")
     74 			throw("runtime: failed to commit pages")
     75 		}
     76 		v = add(v, small)
     77 		n -= small
     78 	}
     79 }
     80 
     81 // Don't split the stack as this function may be invoked without a valid G,
     82 // which prevents us from allocating more stack.
     83 //go:nosplit
     84 func sysFree(v unsafe.Pointer, n uintptr, sysStat *uint64) {
     85 	mSysStatDec(sysStat, n)
     86 	r := stdcall3(_VirtualFree, uintptr(v), 0, _MEM_RELEASE)
     87 	if r == 0 {
     88 		print("runtime: VirtualFree of ", n, " bytes failed with errno=", getlasterror(), "\n")
     89 		throw("runtime: failed to release pages")
     90 	}
     91 }
     92 
     93 func sysFault(v unsafe.Pointer, n uintptr) {
     94 	// SysUnused makes the memory inaccessible and prevents its reuse
     95 	sysUnused(v, n)
     96 }
     97 
     98 func sysReserve(v unsafe.Pointer, n uintptr, reserved *bool) unsafe.Pointer {
     99 	*reserved = true
    100 	// v is just a hint.
    101 	// First try at v.
    102 	v = unsafe.Pointer(stdcall4(_VirtualAlloc, uintptr(v), n, _MEM_RESERVE, _PAGE_READWRITE))
    103 	if v != nil {
    104 		return v
    105 	}
    106 
    107 	// Next let the kernel choose the address.
    108 	return unsafe.Pointer(stdcall4(_VirtualAlloc, 0, n, _MEM_RESERVE, _PAGE_READWRITE))
    109 }
    110 
    111 func sysMap(v unsafe.Pointer, n uintptr, reserved bool, sysStat *uint64) {
    112 	mSysStatInc(sysStat, n)
    113 	p := stdcall4(_VirtualAlloc, uintptr(v), n, _MEM_COMMIT, _PAGE_READWRITE)
    114 	if p != uintptr(v) {
    115 		print("runtime: VirtualAlloc of ", n, " bytes failed with errno=", getlasterror(), "\n")
    116 		throw("runtime: cannot map pages in arena address space")
    117 	}
    118 }
    119