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

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

/*
 * __wt_fsync --
 *	POSIX fsync.
 */
static 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 inline int
__wt_fextend(WT_SESSION_IMPL *session, WT_FH *fh, wt_off_t offset)
{
	WT_FILE_HANDLE *handle;

	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;
	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 (ENOTSUP);
}

/*
 * __wt_file_lock --
 *	Lock/unlock a file.
 */
static 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 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(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);

	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 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 inline int
__wt_ftruncate(WT_SESSION_IMPL *session, WT_FH *fh, wt_off_t offset)
{
	WT_FILE_HANDLE *handle;

	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;
	if (handle->fh_truncate != NULL)
		return (handle->fh_truncate(
		    handle, (WT_SESSION *)session, offset));
	return (ENOTSUP);
}

/*
 * __wt_write --
 *	POSIX pwrite.
 */
static 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(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));
	WT_STAT_CONN_DECR_ATOMIC(session, thread_write_active);
	return (ret);
}
