Add stopwatch APIs for timing
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5c09b41..a1c9e42 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -55,8 +55,10 @@
IF(WIN32)
TARGET_LINK_LIBRARIES(${target} ws2_32)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
- TARGET_LINK_LIBRARIES(${target} socket nsl)
- ENDIF ()
+ TARGET_LINK_LIBRARIES(${target} socket nsl rt)
+ ELSE()
+ TARGET_LINK_LIBRARIES(${target} rt)
+ ENDIF()
IF(THREADSAFE)
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
ENDIF()
diff --git a/src/timing.c b/src/timing.c
new file mode 100644
index 0000000..cf729ea
--- /dev/null
+++ b/src/timing.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "timing.h"
+
+#ifdef GIT_WIN32
+
+#define TICKS_PER_SECOND 1000
+
+int git_stopwatch_start(git_stopwatch *s)
+{
+ /* Try first to use the high-performance counter */
+ if (!QueryPerformanceFrequency(&s->freq) ||
+ !QueryPerformanceCounter(&s->start)) {
+ /* Fall back to GetTickCount */
+ s->freq.QuadPart = 0;
+ s->start.LowPart = GetTickCount();
+ }
+
+ s->running = 1;
+
+ return 0;
+}
+
+int git_stopwatch_query(double *out_elapsed_seconds, git_stopwatch *s)
+{
+ LARGE_INTEGER end;
+ double elapsed_seconds;
+
+ if (!s->running) {
+ giterr_set(GITERR_INVALID, "Stopwatch is not running");
+ return -1;
+ }
+
+ if (s->freq.QuadPart) {
+ if (!QueryPerformanceCounter(&end)) {
+ giterr_set(GITERR_OS, "Unable to read performance counter");
+ return -1;
+ }
+
+ elapsed_seconds = (end.QuadPart - s->start.QuadPart) / (double)s->freq.QuadPart;
+ } else {
+ DWORD end_ticks = GetTickCount();
+
+ /* Unsigned subtraction handles tick count wraparound */
+ elapsed_seconds = (end_ticks - s->start.LowPart) / (double)TICKS_PER_SECOND;
+ }
+
+ /* Just to be on the safe side */
+ if (elapsed_seconds < 0)
+ elapsed_seconds = 0;
+
+ *out_elapsed_seconds = elapsed_seconds;
+
+ return 0;
+}
+
+#elif __APPLE__
+
+int git_stopwatch_start(git_stopwatch *s)
+{
+ (void)mach_timebase_info(&s->info);
+ s->start = mach_absolute_time();
+
+ s->running = 1;
+
+ return 0;
+}
+
+int git_stopwatch_query(double *out_elapsed_seconds, git_stopwatch *s)
+{
+ uint64_t end;
+ double elapsed_seconds;
+
+ if (!s->running) {
+ giterr_set(GITERR_INVALID, "Stopwatch is not running");
+ return -1;
+ }
+
+ end = mach_absolute_time();
+ elapsed_seconds = 0;
+
+ if (end > start) {
+ elapsed_seconds = (double)s->info.numer * (end - start);
+ elapsed_seconds /= (double)s->info.denom * 1E9L;
+ }
+
+ *out_elapsed_seconds = elapsed_seconds;
+
+ return 0;
+}
+
+#else
+
+int git_stopwatch_start(git_stopwatch *s)
+{
+ if (clock_gettime(CLOCK_MONOTONIC, &s->start)) {
+ giterr_set(GITERR_OS, "Unable to get the monotonic timer");
+ return -1;
+ }
+
+ s->running = 1;
+
+ return 0;
+}
+
+int git_stopwatch_query(double *out_elapsed_seconds, git_stopwatch *s)
+{
+ struct timespec end, elapsed;
+ double elapsed_seconds;
+
+ if (!s->running) {
+ giterr_set(GITERR_INVALID, "Stopwatch is not running");
+ return -1;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &end)) {
+ giterr_set(GITERR_OS, "Unable to get the monotonic timer");
+ return -1;
+ }
+
+ elapsed_seconds = 0;
+
+ /* If end < start, then we consider the elapsed time to be zero */
+ if (end.tv_sec > s->start.tv_sec ||
+ (end.tv_sec == s->start.tv_sec && end.tv_nsec > s->start.tv_nsec)) {
+ elapsed.tv_sec = end.tv_sec - s->start.tv_sec;
+ elapsed.tv_nsec = end.tv_nsec - s->start.tv_nsec;
+
+ if (elapsed.tv_nsec < 0) {
+ elapsed.tv_sec--;
+ elapsed.tv_nsec += 1E9L;
+ }
+
+ elapsed_seconds = (double)elapsed.tv_sec + elapsed.tv_nsec / (double)1E9;
+ }
+
+ *out_elapsed_seconds = elapsed_seconds;
+
+ return 0;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/timing.h b/src/timing.h
new file mode 100644
index 0000000..6ee631a
--- /dev/null
+++ b/src/timing.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_timing_h__
+#define INCLUDE_timing_h__
+
+#include "common.h"
+
+#ifndef GIT_WIN32
+# include <time.h>
+#endif
+
+#ifdef __APPLE__
+# include <mach/mach_time.h>
+#endif
+
+#ifdef GIT_WIN32
+
+typedef struct git_stopwatch {
+ LARGE_INTEGER freq;
+ LARGE_INTEGER start;
+ unsigned running : 1;
+} git_stopwatch;
+
+#elif __APPLE__
+
+typedef struct git_stopwatch {
+ mach_timebase_info_data_t info;
+ uint64_t start;
+ unsigned running : 1;
+} git_stopwatch;
+
+#else
+
+typedef struct git_stopwatch {
+ struct timespec start;
+ unsigned running : 1;
+} git_stopwatch;
+
+#endif
+
+extern int git_stopwatch_start(git_stopwatch *s);
+extern int git_stopwatch_query(double *out_elapsed_seconds, git_stopwatch *s);
+
+#endif
diff --git a/tests-clar/online/clone.c b/tests-clar/online/clone.c
index dc5aa41..e4d79cc 100644
--- a/tests-clar/online/clone.c
+++ b/tests-clar/online/clone.c
@@ -5,6 +5,7 @@
#include "remote.h"
#include "fileops.h"
#include "refs.h"
+#include "timing.h"
#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
@@ -39,7 +40,10 @@
void test_online_clone__network_full(void)
{
git_remote *origin;
+ git_stopwatch s;
+ double elapsed_seconds;
+ cl_git_pass(git_stopwatch_start(&s));
cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
cl_assert(!git_repository_is_bare(g_repo));
cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
@@ -47,6 +51,9 @@
cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, origin->download_tags);
git_remote_free(origin);
+
+ cl_git_pass(git_stopwatch_query(&elapsed_seconds, &s));
+ cl_assert(elapsed_seconds > 0);
}
void test_online_clone__network_bare(void)