Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use custom allocator on Windows #96

Merged
merged 3 commits into from
May 20, 2024

Conversation

anuraaga
Copy link
Collaborator

@anuraaga anuraaga commented May 17, 2024

Also makes sure to cap max pages on 32-bit systems regardless for extra safety

@anuraaga
Copy link
Collaborator Author

@ncruces would you be able to take a look at this PR? BTW, the only testing I've done is the windows job in the CI here

cap = (cap + rnd) &^ rnd

if max > math.MaxInt {
// This ensures int(max) overflows to a negative value.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment could be tweaked: unix.Mmap uses signed int (hence my wording); Windows is using uintptr and SIZE_T (both unsigned).

The code still makes sense: it ensures that on 32-bit platforms a very large value overflows to either -1 (Unix) or MaxUint32 (both of which cause a noisy error, rather than the silent corruption).

// Update committed memory.
m.buf = m.buf[:new]
}
return m.buf[:size]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend (which I've also done on Unix):

// Limit returned capacity because bytes beyond
// len(m.buf) have not yet been committed.
return m.buf[:size:len(m.buf)]

That way, Go bounds checks will protect the slice.

r, _, err := procVirtualFree.Call(m.addr, 0, windows_MEM_RELEASE)
if r == 0 {
panic(fmt.Errorf("alloc_windows: failed to release memory: %w", err))
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the other version I'm doing m.buf = nil which ensures subsequent calls return EINVAL.
I dunno if m.addr = 0 would offer the same semantics, but I think it does.

}

func (m *virtualMemory) Reallocate(size uint64) []byte {
if com := uint64(len(m.buf)); com < size {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also tweaked this line to ensure I don't try to over commit (and no overflow):

com := uint64(len(m.buf))
res := uint64(cap(m.buf))
if com < size && size < res {
	// ...
}

@anuraaga
Copy link
Collaborator Author

anuraaga commented May 20, 2024

Thanks a lot for the help @ncruces! added the cleanups

@anuraaga anuraaga merged commit 33902e4 into wasilibs:main May 20, 2024
11 checks passed
@ncruces
Copy link

ncruces commented May 20, 2024

I was going over this again, and as the wazero API shifted for a while, I guess this slipped.

It turns out that we don't need to commit memory in the constructor unless we really want to, because Reallocate is called right afterwards: ncruces/go-sqlite3@d23bdcd

So it's up to you if you want to use WithMemoryCapacityFromMax as a signal to commit all memory right away (in that case you want to commit in the constructor), or if you rather to skip this in the constructor as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants