rlimit_unix.go

 1//go:build unix
 2
 3// This file contains code inspired by Syncthing's rlimit implementation
 4// Syncthing is licensed under the Mozilla Public License Version 2.0
 5// See: https://github.com/syncthing/syncthing/blob/main/LICENSE
 6
 7package watcher
 8
 9import (
10	"runtime"
11	"syscall"
12)
13
14const (
15	// macOS has a specific limit for RLIMIT_NOFILE
16	darwinOpenMax = 10240
17)
18
19// MaximizeOpenFileLimit tries to set the resource limit RLIMIT_NOFILE (number
20// of open file descriptors) to the max (hard limit), if the current (soft
21// limit) is below the max. Returns the new (though possibly unchanged) limit,
22// or an error if it could not be changed.
23func MaximizeOpenFileLimit() (int, error) {
24	// Get the current limit on number of open files.
25	var lim syscall.Rlimit
26	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil {
27		return 0, err
28	}
29
30	// If we're already at max, there's no need to try to raise the limit.
31	if lim.Cur >= lim.Max {
32		return int(lim.Cur), nil
33	}
34
35	// macOS doesn't like a soft limit greater than OPEN_MAX
36	if runtime.GOOS == "darwin" && lim.Max > darwinOpenMax {
37		lim.Max = darwinOpenMax
38	}
39
40	// Try to increase the limit to the max.
41	oldLimit := lim.Cur
42	lim.Cur = lim.Max
43	if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil {
44		return int(oldLimit), err
45	}
46
47	// If the set succeeded, perform a new get to see what happened. We might
48	// have gotten a value lower than the one in lim.Max, if lim.Max was
49	// something that indicated "unlimited" (i.e. intmax).
50	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil {
51		// We don't really know the correct value here since Getrlimit
52		// mysteriously failed after working once... Shouldn't ever happen.
53		return 0, err
54	}
55
56	return int(lim.Cur), nil
57}