/*-
 * Copyright (c) 2014-present MongoDB, Inc.
 * Copyright (c) 2008-2014 WiredTiger, Inc.
 *	All rights reserved.
 *
 * See the file LICENSE for redistribution information.
 */

#pragma once

/*
 * Define functions that increment histogram statistics for filesystem operations latency.
 */
WT_STAT_MSECS_HIST_INCR_FUNC(fsread, perf_hist_fsread_latency)
WT_STAT_MSECS_HIST_INCR_FUNC(fswrite, perf_hist_fswrite_latency)

/*
 * __wt_fsync --
 *     POSIX fsync.
 */
static WT_INLINE int
__wt_fsync(WT_SESSION_IMPL *session, WT_FH *fh, bool block)
{
    WT_DECL_RET;
    WT_FILE_HANDLE *handle;

    WT_ASSERT(session, !F_ISSET(S2C(session), WT_CONN_READONLY));

    __wt_verbose(session, WT_VERB_HANDLEOPS, "%s: handle-sync", fh->handle->name);

    handle = fh->handle;
    /*
     * There is no way to check when the non-blocking sync-file-range is complete, but we track the
     * time taken in the call for completeness.
     */
    WT_STAT_CONN_INCR_ATOMIC(session, thread_fsync_active);
    WT_STAT_CONN_INCR(session, fsync_io);
    if (block)
        ret = (handle->fh_sync == NULL ? 0 : handle->fh_sync(handle, (WT_SESSION *)session));
    else
        ret =
          (handle->fh_sync_nowait == NULL ? 0 :
                                            handle->fh_sync_nowait(handle, (WT_SESSION *)session));
    WT_STAT_CONN_DECR_ATOMIC(session, thread_fsync_active);
    return (ret);
}

/*
 * __wt_fextend --
 *     Extend a file.
 */
static WT_INLINE int
__wt_fextend(WT_SESSION_IMPL *session, WT_FH *fh, wt_off_t offset)
{
    WT_FILE_HANDLE *handle;
#ifdef HAVE_DIAGNOSTIC
    wt_off_t cur_size;
#endif

    WT_ASSERT(session, !F_ISSET(S2C(session), WT_CONN_READONLY));
    WT_ASSERT(session, !F_ISSET(S2C(session), WT_CONN_IN_MEMORY));

    __wt_verbose(session, WT_VERB_HANDLEOPS, "%s: handle-extend: to %" PRIuMAX, fh->handle->name,
      (uintmax_t)offset);

    /*
     * Our caller is responsible for handling any locking issues, all we have to do is find a
     * function to call.
     */
    handle = fh->handle;
#ifdef HAVE_DIAGNOSTIC
    /* Make sure we don't try to shrink the file during backup. */
    if (handle->fh_size != NULL) {
        WT_RET(handle->fh_size(handle, (WT_SESSION *)session, &cur_size));
        WT_ASSERT(
          session, cur_size <= offset || __wt_atomic_load64(&S2C(session)->hot_backup_start) == 0);
    }
#endif
    if (handle->fh_extend_nolock != NULL)
        return (handle->fh_extend_nolock(handle, (WT_SESSION *)session, offset));
    if (handle->fh_extend != NULL)
        return (handle->fh_extend(handle, (WT_SESSION *)session, offset));
    return (__wt_set_return(session, ENOTSUP));
}

/*
 * __wt_file_lock --
 *     Lock/unlock a file.
 */
static WT_INLINE int
__wt_file_lock(WT_SESSION_IMPL *session, WT_FH *fh, bool lock)
{
    WT_FILE_HANDLE *handle;

    __wt_verbose(session, WT_VERB_HANDLEOPS, "%s: handle-lock: %s", fh->handle->name,
      lock ? "lock" : "unlock");

    handle = fh->handle;
    return (handle->fh_lock == NULL ? 0 : handle->fh_lock(handle, (WT_SESSION *)session, lock));
}

/*
 * __wt_read --
 *     POSIX pread.
 */
static WT_INLINE int
__wt_read(WT_SESSION_IMPL *session, WT_FH *fh, wt_off_t offset, size_t len, void *buf)
{
    WT_DECL_RET;
    uint64_t time_start, time_stop;

    __wt_verbose_debug2(session, WT_VERB_HANDLEOPS,
      "%s: handle-read: %" WT_SIZET_FMT " at %" PRIuMAX, fh->handle->name, len, (uintmax_t)offset);

    WT_STAT_CONN_INCR_ATOMIC(session, thread_read_active);
    WT_STAT_CONN_INCR(session, read_io);
    time_start = __wt_clock(session);

    ret = fh->handle->fh_read(fh->handle, (WT_SESSION *)session, offset, len, buf);

    /* Flag any failed read: if we're in startup, it may be fatal. */
    if (ret != 0)
        F_SET(S2C(session), WT_CONN_DATA_CORRUPTION);

    time_stop = __wt_clock(session);
    __wt_stat_msecs_hist_incr_fsread(session, WT_CLOCKDIFF_MS(time_stop, time_start));
    WT_STAT_CONN_DECR_ATOMIC(session, thread_read_active);
    return (ret);
}

/*
 * __wt_filesize --
 *     Get the size of a file in bytes, by file handle.
 */
static WT_INLINE int
__wt_filesize(WT_SESSION_IMPL *session, WT_FH *fh, wt_off_t *sizep)
{
    __wt_verbose(session, WT_VERB_HANDLEOPS, "%s: handle-size", fh->handle->name);

    return (fh->handle->fh_size(fh->handle, (WT_SESSION *)session, sizep));
}

/*
 * __wt_ftruncate --
 *     Truncate a file.
 */
static WT_INLINE int
__wt_ftruncate(WT_SESSION_IMPL *session, WT_FH *fh, wt_off_t offset)
{
    WT_FILE_HANDLE *handle;
#ifdef HAVE_DIAGNOSTIC
    wt_off_t cur_size;
#endif

    WT_ASSERT(session, !F_ISSET(S2C(session), WT_CONN_READONLY));

    __wt_verbose(session, WT_VERB_HANDLEOPS, "%s: handle-truncate: to %" PRIuMAX, fh->handle->name,
      (uintmax_t)offset);

    /*
     * Our caller is responsible for handling any locking issues, all we have to do is find a
     * function to call.
     */
    handle = fh->handle;
#ifdef HAVE_DIAGNOSTIC
    /* Make sure we don't try to shrink the file during backup. */
    if (handle->fh_size != NULL) {
        WT_RET(handle->fh_size(handle, (WT_SESSION *)session, &cur_size));
        WT_ASSERT(
          session, cur_size <= offset || __wt_atomic_load64(&S2C(session)->hot_backup_start) == 0);
    }
#endif
    if (handle->fh_truncate != NULL)
        return (handle->fh_truncate(handle, (WT_SESSION *)session, offset));
    return (__wt_set_return(session, ENOTSUP));
}

/*
 * __wt_write --
 *     POSIX pwrite.
 */
static WT_INLINE int
__wt_write(WT_SESSION_IMPL *session, WT_FH *fh, wt_off_t offset, size_t len, const void *buf)
{
    WT_DECL_RET;
    uint64_t time_start, time_stop;

    WT_ASSERT(session,
      !F_ISSET(S2C(session), WT_CONN_READONLY) ||
        WT_STRING_MATCH(fh->name, WT_SINGLETHREAD, strlen(WT_SINGLETHREAD)));

    __wt_verbose_debug2(session, WT_VERB_HANDLEOPS,
      "%s: handle-write: %" WT_SIZET_FMT " at %" PRIuMAX, fh->handle->name, len, (uintmax_t)offset);

    /*
     * Do a final panic check before I/O, so we stop writing as quickly as possible if there's an
     * unanticipated error. We aren't handling the error correctly by definition, and writing won't
     * make things better.
     */
    WT_RET(WT_SESSION_CHECK_PANIC(session));

    WT_STAT_CONN_INCR(session, write_io);
    WT_STAT_CONN_INCR_ATOMIC(session, thread_write_active);
    time_start = __wt_clock(session);

    ret = fh->handle->fh_write(fh->handle, (WT_SESSION *)session, offset, len, buf);

    time_stop = __wt_clock(session);
    __wt_stat_msecs_hist_incr_fswrite(session, WT_CLOCKDIFF_MS(time_stop, time_start));
    (void)__wt_atomic_addv64(&fh->written, len);
    WT_STAT_CONN_DECR_ATOMIC(session, thread_write_active);
    return (ret);
}
