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}