internal/vfs/osfs/sync_darwin.go (35 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package osfs
import (
"golang.org/x/sys/unix"
"github.com/elastic/go-txfile/internal/vfs"
)
type syncState struct{}
// Sync uses fnctl or fsync in order to flush the file buffers to disk.
// According to the darwin fsync man page[1], usage of sync is not safe. On
// darwin, fsync will only flush the OS file cache to disk, but this won't
// enforce a cache flush on the drive itself. Without forcing the cache flush,
// writes can still be out of order or get lost on power failure.
// According to the man page[1] fcntl with F_FULLFSYNC[2] is required. F_FULLFSYNC
// might not be supported for the current file system. In this case we will
// fallback to fsync.
//
// [1]: https://www.unix.com/man-page/osx/2/fsync
// [2]: https://www.unix.com/man-page/osx/2/fcntl
func (f *File) Sync(flags vfs.SyncFlag) error {
err := f.doSync(flags)
return f.wrapErr("file/sync", err)
}
func (f *File) doSync(flags vfs.SyncFlag) error {
for {
_, err := unix.FcntlInt(f.File.Fd(), unix.F_FULLFSYNC, 0)
err = normalizeSysError(err)
if err == nil || isIOError(err) {
return err
}
if isRetryErr(err) {
continue
}
// XXX: shall we 'guard' the second fsync via ENOTTY, EINVAL, ENXIO ?
// Question: What happens to the error status when calling fsync,
// if F_FULLFSYNC did actually fail due to an IO error, not
// captured by isIOError?
err = f.File.Sync()
if isRetryErr(err) {
continue
}
return err
}
}
func isIOError(err error) bool {
return err == unix.EIO ||
// space/quota
err == unix.ENOSPC || err == unix.EDQUOT || err == unix.EFBIG ||
// network
err == unix.ECONNRESET || err == unix.ENETDOWN || err == unix.ENETUNREACH
}
func isRetryErr(err error) bool {
return err == unix.EINTR || err == unix.EAGAIN
}