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 			throw("runtime: failed to decommit pages")
     52 		}
     53 		v = add(v, small)
     54 		n -= small
     55 	}
     56 }
     57 
     58 func sysUsed(v unsafe.Pointer, n uintptr) {
     59 	r := stdcall4(_VirtualAlloc, uintptr(v), n, _MEM_COMMIT, _PAGE_READWRITE)
     60 	if r != uintptr(v) {
     61 		throw("runtime: failed to commit pages")
     62 	}
     63 
     64 	// Commit failed. See SysUnused.
     65 	for n > 0 {
     66 		small := n
     67 		for small >= 4096 && stdcall4(_VirtualAlloc, uintptr(v), small, _MEM_COMMIT, _PAGE_READWRITE) == 0 {
     68 			small /= 2
     69 			small &^= 4096 - 1
     70 		}
     71 		if small < 4096 {
     72 			throw("runtime: failed to decommit pages")
     73 		}
     74 		v = add(v, small)
     75 		n -= small
     76 	}
     77 }
     78 
     79 // Don't split the stack as this function may be invoked without a valid G,
     80 // which prevents us from allocating more stack.
     81 //go:nosplit
     82 func sysFree(v unsafe.Pointer, n uintptr, sysStat *uint64) {
     83 	mSysStatDec(sysStat, n)
     84 	r := stdcall3(_VirtualFree, uintptr(v), 0, _MEM_RELEASE)
     85 	if r == 0 {
     86 		throw("runtime: failed to release pages")
     87 	}
     88 }
     89 
     90 func sysFault(v unsafe.Pointer, n uintptr) {
     91 	// SysUnused makes the memory inaccessible and prevents its reuse
     92 	sysUnused(v, n)
     93 }
     94 
     95 func sysReserve(v unsafe.Pointer, n uintptr, reserved *bool) unsafe.Pointer {
     96 	*reserved = true
     97 	// v is just a hint.
     98 	// First try at v.
     99 	v = unsafe.Pointer(stdcall4(_VirtualAlloc, uintptr(v), n, _MEM_RESERVE, _PAGE_READWRITE))
    100 	if v != nil {
    101 		return v
    102 	}
    103 
    104 	// Next let the kernel choose the address.
    105 	return unsafe.Pointer(stdcall4(_VirtualAlloc, 0, n, _MEM_RESERVE, _PAGE_READWRITE))
    106 }
    107 
    108 func sysMap(v unsafe.Pointer, n uintptr, reserved bool, sysStat *uint64) {
    109 	mSysStatInc(sysStat, n)
    110 	p := stdcall4(_VirtualAlloc, uintptr(v), n, _MEM_COMMIT, _PAGE_READWRITE)
    111 	if p != uintptr(v) {
    112 		throw("runtime: cannot map pages in arena address space")
    113 	}
    114 }
    115