fix crash on x86_64 android emulators (#4233)
Due to presence of syscall to `epoll_wait` android emulators after Android 10 would crash. This commit has added `amd64` fallback implementations in 2 spots - `tcp-shaker` ref :571aa46f35
- `notify` ref :0eb36e6c1a
The build flags are updated for fallback implementation to only be used in case of `x86_64` simulators. status-mobile PR : https://github.com/status-im/status-mobile/pull/17773
This commit is contained in:
parent
d2ae23f466
commit
a6df2be92e
4
go.mod
4
go.mod
|
@ -4,6 +4,8 @@ go 1.19
|
|||
|
||||
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.11
|
||||
|
||||
replace github.com/rjeczalik/notify => github.com/status-im/notify v1.0.2-status
|
||||
|
||||
replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1
|
||||
|
||||
replace github.com/nfnt/resize => github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088
|
||||
|
@ -51,7 +53,7 @@ require (
|
|||
github.com/status-im/migrate/v4 v4.6.2-status.3
|
||||
github.com/status-im/rendezvous v1.3.7
|
||||
github.com/status-im/status-go/extkeys v1.1.2
|
||||
github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501
|
||||
github.com/status-im/tcp-shaker v1.1.1-status
|
||||
github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a
|
||||
|
|
10
go.sum
10
go.sum
|
@ -1855,10 +1855,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq
|
|||
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
|
||||
github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=
|
||||
github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
|
||||
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
|
@ -1998,14 +1994,16 @@ github.com/status-im/markdown v0.0.0-20231114210825-6c2d15b5dc57/go.mod h1:5rjPy
|
|||
github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8=
|
||||
github.com/status-im/migrate/v4 v4.6.2-status.3 h1:Khwjb59NzniloUr5i9s9AtkEyqBbQFt1lkoAu66sAu0=
|
||||
github.com/status-im/migrate/v4 v4.6.2-status.3/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8=
|
||||
github.com/status-im/notify v1.0.2-status h1:x8wev0Sh8H8KAf4bVcv+L0dVHldBESOKUlqRqRY7uL8=
|
||||
github.com/status-im/notify v1.0.2-status/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
|
||||
github.com/status-im/rendezvous v1.3.7 h1:rZGWsFCjPV3MWeUkLkZSOGTAvyRf+rxx5hnEGLE4OHg=
|
||||
github.com/status-im/rendezvous v1.3.7/go.mod h1:r0vCbQJByTteMajN0f+Mcet/Vd7uAXxFPfewNpI2iXQ=
|
||||
github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 h1:ClCAP2FPCvl8hGMhbUx/tq/sOu2wibztAa5jAvQEe4Q=
|
||||
github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088/go.mod h1:+92j1tN27DypDeBFxkg0uzkqfh1bNHTZe3Bv2PjvxpM=
|
||||
github.com/status-im/status-go/extkeys v1.1.2 h1:FSjARgDathJ3rIapJt851LsIXP9Oyuu2M2jPJKuzloU=
|
||||
github.com/status-im/status-go/extkeys v1.1.2/go.mod h1:hCmFzb2jiiVF2voZKYbzuhOQiHHCmyLJsZJXrFFg7BY=
|
||||
github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 h1:oa0KU5jJRNtXaM/P465MhvSFo/HM2O8qi2DDuPcd7ro=
|
||||
github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk=
|
||||
github.com/status-im/tcp-shaker v1.1.1-status h1:TnVeeWlq2SKCWotHc4Vi6qZQfY8TTe3VLmu1xpEFYhg=
|
||||
github.com/status-im/tcp-shaker v1.1.1-status/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk=
|
||||
github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857 h1:sPkzT7Z7uLmejOsBRlZ0kwDWpqjpHJsp834o5nbhqho=
|
||||
github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857/go.mod h1:lq9I5ROto5tcua65GmCE6SIW7VE0ucdEBs1fn4z7uWU=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -22,6 +23,8 @@ import (
|
|||
|
||||
const defaultBackoff = 10 * time.Second
|
||||
const graylistBackoff = 3 * time.Minute
|
||||
const isAndroidEmulator = runtime.GOOS == "android" && runtime.GOARCH == "amd64"
|
||||
const findNearestMailServer = !isAndroidEmulator
|
||||
|
||||
func (m *Messenger) mailserversByFleet(fleet string) []mailservers.Mailserver {
|
||||
var items []mailservers.Mailserver
|
||||
|
@ -235,76 +238,104 @@ func (m *Messenger) findNewMailserver() error {
|
|||
|
||||
allMailservers := m.mailserverCycle.allMailservers
|
||||
|
||||
m.logger.Info("Finding a new mailserver...")
|
||||
// TODO: remove this check once sockets are stable on x86_64 emulators
|
||||
if findNearestMailServer {
|
||||
m.logger.Info("Finding a new mailserver...")
|
||||
|
||||
var mailserverStr []string
|
||||
for _, m := range allMailservers {
|
||||
mailserverStr = append(mailserverStr, m.Address)
|
||||
}
|
||||
|
||||
if len(allMailservers) == 0 {
|
||||
m.logger.Warn("no mailservers available") // Do nothing...
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
var parseFn func(string) (string, error)
|
||||
if allMailservers[0].Version == 2 {
|
||||
parseFn = mailservers.MultiAddressToAddress
|
||||
} else {
|
||||
parseFn = mailservers.EnodeStringToAddr
|
||||
}
|
||||
|
||||
pingResult, err := mailservers.DoPing(context.Background(), mailserverStr, 500, parseFn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var availableMailservers []*mailservers.PingResult
|
||||
for _, result := range pingResult {
|
||||
if result.Err != nil {
|
||||
m.logger.Info("connecting error", zap.String("err", *result.Err))
|
||||
continue // The results with error are ignored
|
||||
var mailserverStr []string
|
||||
for _, m := range allMailservers {
|
||||
mailserverStr = append(mailserverStr, m.Address)
|
||||
}
|
||||
availableMailservers = append(availableMailservers, result)
|
||||
}
|
||||
|
||||
if len(availableMailservers) == 0 {
|
||||
m.logger.Warn("No mailservers available") // Do nothing...
|
||||
return nil
|
||||
if len(allMailservers) == 0 {
|
||||
m.logger.Warn("no mailservers available") // Do nothing...
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
var parseFn func(string) (string, error)
|
||||
if allMailservers[0].Version == 2 {
|
||||
parseFn = mailservers.MultiAddressToAddress
|
||||
} else {
|
||||
parseFn = mailservers.EnodeStringToAddr
|
||||
}
|
||||
|
||||
pingResult, err := mailservers.DoPing(context.Background(), mailserverStr, 500, parseFn)
|
||||
if err != nil {
|
||||
// pinging mailservers might fail, but we don't care
|
||||
m.logger.Warn("mailservers.DoPing failed with", zap.Error(err))
|
||||
}
|
||||
|
||||
var availableMailservers []*mailservers.PingResult
|
||||
for _, result := range pingResult {
|
||||
if result.Err != nil {
|
||||
m.logger.Info("connecting error", zap.String("err", *result.Err))
|
||||
continue // The results with error are ignored
|
||||
}
|
||||
availableMailservers = append(availableMailservers, result)
|
||||
}
|
||||
|
||||
if len(availableMailservers) == 0 {
|
||||
m.logger.Warn("No mailservers available") // Do nothing...
|
||||
return nil
|
||||
}
|
||||
|
||||
mailserversByAddress := make(map[string]mailservers.Mailserver)
|
||||
for idx := range allMailservers {
|
||||
mailserversByAddress[allMailservers[idx].Address] = allMailservers[idx]
|
||||
}
|
||||
var sortedMailservers []SortedMailserver
|
||||
for _, ping := range availableMailservers {
|
||||
address := ping.Address
|
||||
ms := mailserversByAddress[address]
|
||||
sortedMailserver := SortedMailserver{
|
||||
Address: address,
|
||||
RTTMs: *ping.RTTMs,
|
||||
}
|
||||
m.mailPeersMutex.Lock()
|
||||
pInfo, ok := m.mailserverCycle.peers[ms.ID]
|
||||
m.mailPeersMutex.Unlock()
|
||||
if ok {
|
||||
if time.Now().Before(pInfo.canConnectAfter) {
|
||||
continue // We can't connect to this node yet
|
||||
}
|
||||
}
|
||||
|
||||
sortedMailservers = append(sortedMailservers, sortedMailserver)
|
||||
|
||||
}
|
||||
sort.Sort(byRTTMsAndCanConnectBefore(sortedMailservers))
|
||||
|
||||
// Picks a random mailserver amongs the ones with the lowest latency
|
||||
// The pool size is 1/4 of the mailservers were pinged successfully
|
||||
pSize := poolSize(len(sortedMailservers) - 1)
|
||||
if pSize <= 0 {
|
||||
pSize = len(sortedMailservers)
|
||||
if pSize <= 0 {
|
||||
m.logger.Warn("No mailservers available") // Do nothing...
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
r, err := rand.Int(rand.Reader, big.NewInt(int64(pSize)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msPing := sortedMailservers[r.Int64()]
|
||||
ms := mailserversByAddress[msPing.Address]
|
||||
m.logger.Info("connecting to mailserver", zap.String("address", ms.Address))
|
||||
return m.connectToMailserver(ms)
|
||||
}
|
||||
|
||||
mailserversByAddress := make(map[string]mailservers.Mailserver)
|
||||
for idx := range allMailservers {
|
||||
mailserversByAddress[allMailservers[idx].Address] = allMailservers[idx]
|
||||
}
|
||||
var sortedMailservers []SortedMailserver
|
||||
for _, ping := range availableMailservers {
|
||||
address := ping.Address
|
||||
ms := mailserversByAddress[address]
|
||||
sortedMailserver := SortedMailserver{
|
||||
Address: address,
|
||||
RTTMs: *ping.RTTMs,
|
||||
}
|
||||
m.mailPeersMutex.Lock()
|
||||
pInfo, ok := m.mailserverCycle.peers[ms.ID]
|
||||
m.mailPeersMutex.Unlock()
|
||||
if ok {
|
||||
if time.Now().Before(pInfo.canConnectAfter) {
|
||||
continue // We can't connect to this node yet
|
||||
}
|
||||
}
|
||||
|
||||
sortedMailservers = append(sortedMailservers, sortedMailserver)
|
||||
|
||||
}
|
||||
sort.Sort(byRTTMsAndCanConnectBefore(sortedMailservers))
|
||||
|
||||
// Picks a random mailserver amongs the ones with the lowest latency
|
||||
// The pool size is 1/4 of the mailservers were pinged successfully
|
||||
pSize := poolSize(len(sortedMailservers) - 1)
|
||||
pSize := poolSize(len(allMailservers) - 1)
|
||||
if pSize <= 0 {
|
||||
pSize = len(sortedMailservers)
|
||||
pSize = len(allMailservers)
|
||||
if pSize <= 0 {
|
||||
m.logger.Warn("No mailservers available") // Do nothing...
|
||||
return nil
|
||||
|
@ -316,10 +347,11 @@ func (m *Messenger) findNewMailserver() error {
|
|||
return err
|
||||
}
|
||||
|
||||
msPing := sortedMailservers[r.Int64()]
|
||||
msPing := allMailservers[r.Int64()]
|
||||
ms := mailserversByAddress[msPing.Address]
|
||||
m.logger.Info("connecting to mailserver", zap.String("address", ms.Address))
|
||||
return m.connectToMailserver(ms)
|
||||
|
||||
}
|
||||
|
||||
func (m *Messenger) activeMailserverStatus() (connStatus, error) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
notify [![GoDoc](https://godoc.org/github.com/rjeczalik/notify?status.svg)](https://godoc.org/github.com/rjeczalik/notify) [![Build Status](https://img.shields.io/travis/rjeczalik/notify/master.svg)](https://travis-ci.org/rjeczalik/notify "inotify + FSEvents + kqueue") [![Build status](https://img.shields.io/appveyor/ci/rjeczalik/notify-246.svg)](https://ci.appveyor.com/project/rjeczalik/notify-246 "ReadDirectoryChangesW") [![Coverage Status](https://img.shields.io/coveralls/rjeczalik/notify/master.svg)](https://coveralls.io/r/rjeczalik/notify?branch=master)
|
||||
======
|
||||
|
||||
Filesystem event notification library on steroids. (under active development)
|
||||
Filesystem event notification library on steroids.
|
||||
|
||||
*Documentation*
|
||||
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//go:build linux
|
||||
//go:build linux && !(android && amd64)
|
||||
// +build linux
|
||||
// +build !android !amd64
|
||||
|
||||
package notify
|
||||
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//go:build android && amd64
|
||||
// +build android,amd64
|
||||
|
||||
package notify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"golang.org/x/sys/unix"
|
||||
"path/filepath"
|
||||
_ "path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
_ "syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// eventBufferSize defines the size of the buffer given to read(2) function. One
|
||||
// should not depend on this value, since it was arbitrary chosen and may be
|
||||
// changed in the future.
|
||||
const eventBufferSize = 64 * (unix.SizeofInotifyEvent + unix.PathMax + 1)
|
||||
|
||||
// consumersCount defines the number of consumers in producer-consumer based
|
||||
// implementation. Each consumer is run in a separate goroutine and has read
|
||||
// access to watched files map.
|
||||
const consumersCount = 2
|
||||
|
||||
const invalidDescriptor = -1
|
||||
|
||||
// watched is a pair of file path and inotify mask used as a value in
|
||||
// watched files map.
|
||||
type watched struct {
|
||||
path string
|
||||
mask uint32
|
||||
}
|
||||
|
||||
// inotify implements Watcher interface.
|
||||
type inotify struct {
|
||||
sync.RWMutex // protects inotify.m map
|
||||
m map[int32]*watched // watch descriptor to watched object
|
||||
fd int32 // inotify file descriptor
|
||||
pipefd []int // pipe's read and write descriptors
|
||||
epfd int // epoll descriptor
|
||||
epes []unix.EpollEvent // epoll events
|
||||
buffer [eventBufferSize]byte // inotify event buffer
|
||||
wg sync.WaitGroup // wait group used to close main loop
|
||||
c chan<- EventInfo // event dispatcher channel
|
||||
}
|
||||
|
||||
// NewWatcher creates new non-recursive inotify backed by inotify.
|
||||
func newWatcher(c chan<- EventInfo) watcher {
|
||||
i := &inotify{
|
||||
m: make(map[int32]*watched),
|
||||
fd: invalidDescriptor,
|
||||
pipefd: []int{invalidDescriptor, invalidDescriptor},
|
||||
epfd: invalidDescriptor,
|
||||
epes: make([]unix.EpollEvent, 0),
|
||||
c: c,
|
||||
}
|
||||
runtime.SetFinalizer(i, func(i *inotify) {
|
||||
i.epollclose()
|
||||
if i.fd != invalidDescriptor {
|
||||
unix.Close(int(i.fd))
|
||||
}
|
||||
})
|
||||
return i
|
||||
}
|
||||
|
||||
// Watch implements notify.watcher interface.
|
||||
func (i *inotify) Watch(path string, e Event) error {
|
||||
return i.watch(path, e)
|
||||
}
|
||||
|
||||
// Rewatch implements notify.watcher interface.
|
||||
func (i *inotify) Rewatch(path string, _, newevent Event) error {
|
||||
return i.watch(path, newevent)
|
||||
}
|
||||
|
||||
// watch adds a new watcher to the set of watched objects or modifies the existing
|
||||
// one. If called for the first time, this function initializes inotify filesystem
|
||||
// monitor and starts producer-consumers goroutines.
|
||||
func (i *inotify) watch(path string, e Event) (err error) {
|
||||
if e&^(All|Event(unix.IN_ALL_EVENTS)) != 0 {
|
||||
return errors.New("notify: unknown event")
|
||||
}
|
||||
if err = i.lazyinit(); err != nil {
|
||||
return
|
||||
}
|
||||
iwd, err := unix.InotifyAddWatch(int(i.fd), path, encode(e))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i.Lock()
|
||||
if wd, ok := i.m[int32(iwd)]; !ok {
|
||||
i.m[int32(iwd)] = &watched{path: path, mask: uint32(e)}
|
||||
} else {
|
||||
wd.path = path
|
||||
wd.mask = uint32(e)
|
||||
}
|
||||
i.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// lazyinit sets up all required file descriptors and starts 1+consumersCount
|
||||
// goroutines. The producer goroutine blocks until file-system notifications
|
||||
// occur. Then, all events are read from system buffer and sent to consumer
|
||||
// goroutines which construct valid notify events. This method uses
|
||||
// Double-Checked Locking optimization.
|
||||
func (i *inotify) lazyinit() error {
|
||||
if atomic.LoadInt32(&i.fd) == invalidDescriptor {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
if atomic.LoadInt32(&i.fd) == invalidDescriptor {
|
||||
fd, err := unix.InotifyInit1(unix.IN_CLOEXEC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.fd = int32(fd)
|
||||
if err = i.epollinit(); err != nil {
|
||||
_, _ = i.epollclose(), unix.Close(int(fd)) // Ignore errors.
|
||||
i.fd = invalidDescriptor
|
||||
return err
|
||||
}
|
||||
esch := make(chan []*event)
|
||||
go i.loop(esch)
|
||||
i.wg.Add(consumersCount)
|
||||
for n := 0; n < consumersCount; n++ {
|
||||
go i.send(esch)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// epollinit opens an epoll file descriptor and creates a pipe which will be
|
||||
// used to wake up the epoll_wait(2) function. Then, file descriptor associated
|
||||
// with inotify event queue and the read end of the pipe are added to epoll set.
|
||||
// Note that `fd` member must be set before this function is called.
|
||||
func (i *inotify) epollinit() (err error) {
|
||||
if i.epfd, err = unix.EpollCreate1(0); err != nil {
|
||||
return
|
||||
}
|
||||
if err = unix.Pipe(i.pipefd); err != nil {
|
||||
return
|
||||
}
|
||||
i.epes = []unix.EpollEvent{
|
||||
{Events: unix.EPOLLIN, Fd: i.fd},
|
||||
{Events: unix.EPOLLIN, Fd: int32(i.pipefd[0])},
|
||||
}
|
||||
if err = unix.EpollCtl(i.epfd, unix.EPOLL_CTL_ADD, int(i.fd), &i.epes[0]); err != nil {
|
||||
return
|
||||
}
|
||||
return unix.EpollCtl(i.epfd, unix.EPOLL_CTL_ADD, i.pipefd[0], &i.epes[1])
|
||||
}
|
||||
|
||||
// epollclose closes the file descriptor created by the call to epoll_create(2)
|
||||
// and two file descriptors opened by pipe(2) function.
|
||||
func (i *inotify) epollclose() (err error) {
|
||||
if i.epfd != invalidDescriptor {
|
||||
if err = unix.Close(i.epfd); err == nil {
|
||||
i.epfd = invalidDescriptor
|
||||
}
|
||||
}
|
||||
for n, fd := range i.pipefd {
|
||||
if fd != invalidDescriptor {
|
||||
switch e := unix.Close(fd); {
|
||||
case e != nil && err == nil:
|
||||
err = e
|
||||
case e == nil:
|
||||
i.pipefd[n] = invalidDescriptor
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *inotify) loop(esch chan<- []*event) {
|
||||
inotifyCh := make(chan []*event)
|
||||
closeCh := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
for {
|
||||
events := i.read()
|
||||
if events != nil {
|
||||
inotifyCh <- events
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, 1)
|
||||
for {
|
||||
_, err := unix.Read(i.pipefd[0], buf)
|
||||
if err != nil {
|
||||
closeCh <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case events := <-inotifyCh:
|
||||
esch <- events
|
||||
case <-closeCh:
|
||||
i.closeResources(esch)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *inotify) closeResources(esch chan<- []*event) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
fd := atomic.LoadInt32(&i.fd)
|
||||
if err := unix.Close(int(fd)); err != nil && err != unix.EINTR {
|
||||
panic("notify: close(2) error " + err.Error())
|
||||
}
|
||||
|
||||
atomic.StoreInt32(&i.fd, invalidDescriptor)
|
||||
if err := i.epollclose(); err != nil && err != unix.EINTR {
|
||||
panic("notify: epollclose error " + err.Error())
|
||||
}
|
||||
|
||||
close(esch)
|
||||
}
|
||||
|
||||
// read reads events from an inotify file descriptor. It does not handle errors
|
||||
// returned from read(2) function since they are not critical to watcher logic.
|
||||
func (i *inotify) read() (es []*event) {
|
||||
n, err := unix.Read(int(i.fd), i.buffer[:])
|
||||
if err != nil || n < unix.SizeofInotifyEvent {
|
||||
return
|
||||
}
|
||||
var sys *unix.InotifyEvent
|
||||
nmin := n - unix.SizeofInotifyEvent
|
||||
for pos, path := 0, ""; pos <= nmin; {
|
||||
sys = (*unix.InotifyEvent)(unsafe.Pointer(&i.buffer[pos]))
|
||||
pos += unix.SizeofInotifyEvent
|
||||
if path = ""; sys.Len > 0 {
|
||||
endpos := pos + int(sys.Len)
|
||||
path = string(bytes.TrimRight(i.buffer[pos:endpos], "\x00"))
|
||||
pos = endpos
|
||||
}
|
||||
es = append(es, &event{
|
||||
sys: unix.InotifyEvent{
|
||||
Wd: sys.Wd,
|
||||
Mask: sys.Mask,
|
||||
Cookie: sys.Cookie,
|
||||
},
|
||||
path: path,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// send is a consumer function which sends events to event dispatcher channel.
|
||||
// It is run in a separate goroutine in order to not block loop method when
|
||||
// possibly expensive write operations are performed on inotify map.
|
||||
func (i *inotify) send(esch <-chan []*event) {
|
||||
for es := range esch {
|
||||
for _, e := range i.transform(es) {
|
||||
if e != nil {
|
||||
i.c <- e
|
||||
}
|
||||
}
|
||||
}
|
||||
i.wg.Done()
|
||||
}
|
||||
|
||||
// transform prepares events read from inotify file descriptor for sending to
|
||||
// user. It removes invalid events and these which are no longer present in
|
||||
// inotify map. This method may also split one raw event into two different ones
|
||||
// when system-dependent result is required.
|
||||
func (i *inotify) transform(es []*event) []*event {
|
||||
var multi []*event
|
||||
i.RLock()
|
||||
for idx, e := range es {
|
||||
if e.sys.Mask&(unix.IN_IGNORED|unix.IN_Q_OVERFLOW) != 0 {
|
||||
es[idx] = nil
|
||||
continue
|
||||
}
|
||||
wd, ok := i.m[e.sys.Wd]
|
||||
if !ok || e.sys.Mask&encode(Event(wd.mask)) == 0 {
|
||||
es[idx] = nil
|
||||
continue
|
||||
}
|
||||
if e.path == "" {
|
||||
e.path = wd.path
|
||||
} else {
|
||||
e.path = filepath.Join(wd.path, e.path)
|
||||
}
|
||||
multi = append(multi, decode(Event(wd.mask), e))
|
||||
if e.event == 0 {
|
||||
es[idx] = nil
|
||||
}
|
||||
}
|
||||
i.RUnlock()
|
||||
es = append(es, multi...)
|
||||
return es
|
||||
}
|
||||
|
||||
// encode converts notify system-independent events to valid inotify mask
|
||||
// which can be passed to inotify_add_watch(2) function.
|
||||
func encode(e Event) uint32 {
|
||||
if e&Create != 0 {
|
||||
e = (e ^ Create) | InCreate | InMovedTo
|
||||
}
|
||||
if e&Remove != 0 {
|
||||
e = (e ^ Remove) | InDelete | InDeleteSelf
|
||||
}
|
||||
if e&Write != 0 {
|
||||
e = (e ^ Write) | InModify
|
||||
}
|
||||
if e&Rename != 0 {
|
||||
e = (e ^ Rename) | InMovedFrom | InMoveSelf
|
||||
}
|
||||
return uint32(e)
|
||||
}
|
||||
|
||||
// decode uses internally stored mask to distinguish whether system-independent
|
||||
// or system-dependent event is requested. The first one is created by modifying
|
||||
// `e` argument. decode method sets e.event value to 0 when an event should be
|
||||
// skipped. System-dependent event is set as the function's return value which
|
||||
// can be nil when the event should not be passed on.
|
||||
func decode(mask Event, e *event) (syse *event) {
|
||||
if sysmask := uint32(mask) & e.sys.Mask; sysmask != 0 {
|
||||
syse = &event{sys: unix.InotifyEvent{
|
||||
Wd: e.sys.Wd,
|
||||
Mask: e.sys.Mask,
|
||||
Cookie: e.sys.Cookie,
|
||||
}, event: Event(sysmask), path: e.path}
|
||||
}
|
||||
imask := encode(mask)
|
||||
switch {
|
||||
case mask&Create != 0 && imask&uint32(InCreate|InMovedTo)&e.sys.Mask != 0:
|
||||
e.event = Create
|
||||
case mask&Remove != 0 && imask&uint32(InDelete|InDeleteSelf)&e.sys.Mask != 0:
|
||||
e.event = Remove
|
||||
case mask&Write != 0 && imask&uint32(InModify)&e.sys.Mask != 0:
|
||||
e.event = Write
|
||||
case mask&Rename != 0 && imask&uint32(InMovedFrom|InMoveSelf)&e.sys.Mask != 0:
|
||||
e.event = Rename
|
||||
default:
|
||||
e.event = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Unwatch implements notify.watcher interface. It looks for watch descriptor
|
||||
// related to registered path and if found, calls inotify_rm_watch(2) function.
|
||||
// This method is allowed to return EINVAL error when concurrently requested to
|
||||
// delete identical path.
|
||||
func (i *inotify) Unwatch(path string) (err error) {
|
||||
iwd := int32(invalidDescriptor)
|
||||
i.RLock()
|
||||
for iwdkey, wd := range i.m {
|
||||
if wd.path == path {
|
||||
iwd = iwdkey
|
||||
break
|
||||
}
|
||||
}
|
||||
i.RUnlock()
|
||||
if iwd == invalidDescriptor {
|
||||
return errors.New("notify: path " + path + " is already watched")
|
||||
}
|
||||
fd := atomic.LoadInt32(&i.fd)
|
||||
if err = removeInotifyWatch(fd, iwd); err != nil {
|
||||
return
|
||||
}
|
||||
i.Lock()
|
||||
delete(i.m, iwd)
|
||||
i.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements notify.watcher interface. It removes all existing watch
|
||||
// descriptors and wakes up producer goroutine by sending data to the write end
|
||||
// of the pipe. The function waits for a signal from producer which means that
|
||||
// all operations on current monitoring instance are done.
|
||||
func (i *inotify) Close() (err error) {
|
||||
i.Lock()
|
||||
if fd := atomic.LoadInt32(&i.fd); fd == invalidDescriptor {
|
||||
i.Unlock()
|
||||
return nil
|
||||
}
|
||||
for iwd := range i.m {
|
||||
if e := removeInotifyWatch(i.fd, iwd); e != nil && err == nil {
|
||||
err = e
|
||||
}
|
||||
delete(i.m, iwd)
|
||||
}
|
||||
switch _, errwrite := unix.Write(i.pipefd[1], []byte{0x00}); {
|
||||
case errwrite != nil && err == nil:
|
||||
err = errwrite
|
||||
fallthrough
|
||||
case errwrite != nil:
|
||||
i.Unlock()
|
||||
default:
|
||||
i.Unlock()
|
||||
i.wg.Wait()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// if path was removed, notify already removed the watch and returns EINVAL error
|
||||
func removeInotifyWatch(fd int32, iwd int32) (err error) {
|
||||
if _, err = unix.InotifyRmWatch(int(fd), uint32(iwd)); err != nil && err != unix.EINVAL {
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
//go:build linux && !(android && amd64)
|
||||
// +build linux
|
||||
// +build !android !amd64
|
||||
|
||||
package tcp
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
//go:build android && amd64
|
||||
// +build android,amd64
|
||||
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const maxEpollEvents = 32
|
||||
|
||||
// createSocket creates a socket with necessary options set.
|
||||
func createSocketZeroLinger(zeroLinger bool) (fd int, err error) {
|
||||
// Create socket
|
||||
fd, err = _createNonBlockingSocket()
|
||||
if err == nil && zeroLinger {
|
||||
err = _setZeroLinger(fd)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// createNonBlockingSocket creates a non-blocking socket with necessary options all set.
|
||||
func _createNonBlockingSocket() (int, error) {
|
||||
// Create socket
|
||||
fd, err := _createSocket()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Set necessary options
|
||||
err = _setSockOpts(fd)
|
||||
if err != nil {
|
||||
unix.Close(fd)
|
||||
}
|
||||
return fd, err
|
||||
}
|
||||
|
||||
// createSocket creates a socket with CloseOnExec set
|
||||
func _createSocket() (int, error) {
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
|
||||
if err != nil {
|
||||
return 0, os.NewSyscallError("socket", err)
|
||||
}
|
||||
|
||||
unix.CloseOnExec(fd)
|
||||
return fd, err
|
||||
}
|
||||
|
||||
// setSockOpts sets SOCK_NONBLOCK and TCP_QUICKACK for given fd
|
||||
func _setSockOpts(fd int) error {
|
||||
err := unix.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_QUICKACK, 0)
|
||||
}
|
||||
|
||||
var zeroLinger = unix.Linger{Onoff: 1, Linger: 0}
|
||||
|
||||
// setLinger sets SO_Linger with 0 timeout to given fd
|
||||
func _setZeroLinger(fd int) error {
|
||||
return unix.SetsockoptLinger(fd, unix.SOL_SOCKET, unix.SO_LINGER, &zeroLinger)
|
||||
}
|
||||
|
||||
func createPoller() (fd int, err error) {
|
||||
fd, err = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
|
||||
if err != nil {
|
||||
err = os.NewSyscallError("epoll_create1", err)
|
||||
}
|
||||
return fd, err
|
||||
}
|
||||
|
||||
// registerEvents registers given fd with read and write events.
|
||||
func registerEvents(pollerFd int, fd int) error {
|
||||
var event unix.EpollEvent
|
||||
event.Events = unix.EPOLLOUT | unix.EPOLLIN | unix.EPOLLET
|
||||
event.Fd = int32(fd)
|
||||
if err := unix.EpollCtl(pollerFd, unix.EPOLL_CTL_ADD, fd, &event); err != nil {
|
||||
return os.NewSyscallError(fmt.Sprintf("epoll_ctl(%d, ADD, %d, ...)", pollerFd, fd), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func pollEvents(pollerFd int, timeout time.Duration) ([]event, error) {
|
||||
eventCh := make(chan event)
|
||||
errorCh := make(chan error)
|
||||
doneCh := make(chan bool)
|
||||
|
||||
go func(fd int) {
|
||||
for {
|
||||
select {
|
||||
case <-doneCh:
|
||||
return
|
||||
default:
|
||||
n, _, err := unix.Recvfrom(fd, nil, unix.MSG_DONTWAIT|unix.MSG_PEEK)
|
||||
if err != nil && err != unix.EAGAIN && err != unix.EWOULDBLOCK {
|
||||
errorCh <- os.NewSyscallError("recvfrom", err)
|
||||
return
|
||||
}
|
||||
if n > 0 {
|
||||
eventCh <- event{Fd: fd, Err: nil}
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}(pollerFd)
|
||||
|
||||
var events []event
|
||||
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case evt := <-eventCh:
|
||||
events = append(events, evt)
|
||||
case err := <-errorCh:
|
||||
return nil, err
|
||||
case <-timer.C:
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
|
||||
// Signal the goroutine to stop.
|
||||
close(doneCh)
|
||||
|
||||
return events, nil
|
||||
}
|
|
@ -871,7 +871,7 @@ github.com/remyoudompheng/bigfft
|
|||
# github.com/rivo/uniseg v0.2.0
|
||||
## explicit; go 1.12
|
||||
github.com/rivo/uniseg
|
||||
# github.com/rjeczalik/notify v0.9.3
|
||||
# github.com/rjeczalik/notify v0.9.3 => github.com/status-im/notify v1.0.2-status
|
||||
## explicit; go 1.11
|
||||
github.com/rjeczalik/notify
|
||||
# github.com/rs/cors v1.7.0
|
||||
|
@ -935,7 +935,7 @@ github.com/status-im/rendezvous/server
|
|||
# github.com/status-im/status-go/extkeys v1.1.2
|
||||
## explicit; go 1.13
|
||||
github.com/status-im/status-go/extkeys
|
||||
# github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501
|
||||
# github.com/status-im/tcp-shaker v1.1.1-status
|
||||
## explicit; go 1.13
|
||||
github.com/status-im/tcp-shaker
|
||||
# github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857
|
||||
|
@ -1396,6 +1396,7 @@ olympos.io/encoding/edn
|
|||
zombiezen.com/go/sqlite
|
||||
zombiezen.com/go/sqlite/fs
|
||||
zombiezen.com/go/sqlite/sqlitex
|
||||
# github.com/rjeczalik/notify => github.com/status-im/notify v1.0.2-status
|
||||
# github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1
|
||||
# github.com/nfnt/resize => github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088
|
||||
# github.com/forPelevin/gomoji => github.com/status-im/gomoji v1.1.3-0.20220213022530-e5ac4a8732d4
|
||||
|
|
Loading…
Reference in New Issue