aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rwxr-xr-xconfigure9
-rw-r--r--doc/Manual/en/BH_Timer.pod82
-rw-r--r--doc/Manual/en/Makefile2
-rw-r--r--doc/Manual/ru/BH_Timer.pod83
-rw-r--r--doc/Manual/ru/Makefile2
-rw-r--r--include/BH/Timer.h32
-rw-r--r--src/Platform/Dummy/Timer.c50
-rw-r--r--src/Platform/Posix/Timer.c139
-rw-r--r--src/Platform/Win32/Timer.c130
-rw-r--r--test/src/TestTimer.c36
11 files changed, 565 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5450975..d6d50ab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -102,14 +102,17 @@ file(GLOB BH_HEADER
set(BH_SOURCE_DUMMY
src/Platform/Dummy/File.c
+ src/Platform/Dummy/Timer.c
)
set(BH_SOURCE_WIN32
src/Platform/Win32/File.c
+ src/Platform/Win32/Timer.c
)
set(BH_SOURCE_POSIX
src/Platform/Posix/File.c
+ src/Platform/Posix/Timer.c
)
set(BH_SOURCE_WIN32_MT
diff --git a/configure b/configure
index 903cbf2..c4e1d03 100755
--- a/configure
+++ b/configure
@@ -164,9 +164,12 @@ done
add_source "${source_path}src/Platform/Spinlock.c"
case "$platform" in
- Posix) add_source "${source_path}src/Platform/Posix/File.c" ;;
- Win32) add_source "${source_path}src/Platform/Win32/File.c" ;;
- *) add_source "${source_path}src/Platform/Dummy/File.c" ;;
+ Posix) add_source "${source_path}src/Platform/Posix/File.c" ;
+ add_source "${source_path}src/Platform/Posix/Timer.c" ;;
+ Win32) add_source "${source_path}src/Platform/Win32/File.c" ;
+ add_source "${source_path}src/Platform/Win32/Timer.c" ;;
+ *) add_source "${source_path}src/Platform/Dummy/File.c" ;
+ add_source "${source_path}src/Platform/Dummy/Timer.c" ;;
esac
if [ "$enable_mt" = "yes" ]; then
diff --git a/doc/Manual/en/BH_Timer.pod b/doc/Manual/en/BH_Timer.pod
new file mode 100644
index 0000000..56e79fe
--- /dev/null
+++ b/doc/Manual/en/BH_Timer.pod
@@ -0,0 +1,82 @@
+=encoding UTF-8
+
+
+=head1 NAME
+
+BH_Timer - timer utilities
+
+
+=head1 SYNTAX
+
+ #include <BH/Timer.h>
+
+ cc prog.c -o prog -lbh
+
+
+=head1 DESCRIPTION
+
+This module provides a simple high-resolution timer interface for measuring
+elapsed time. The timer uses a monotonic clock source when available, making it
+suitable for accurate time measurements, such as profiling or timeouts. It
+supports millisecond and nanosecond precision and can be started, restarted,
+and queried for elapsed time.
+
+
+
+=head1 API CALLS
+
+
+=head2 BH_TimerNew
+
+ BH_Timer *BH_TimerNew(void);
+
+Creates and initializes a new timer object.
+
+Returns a pointer to a newly allocated BH_Timer on success, or NULL on failure.
+
+
+=head2 BH_TimerFree
+
+ void BH_TimerFree(BH_Timer *timer);
+
+Destroys the timer.
+
+=head2 BH_TimerIsMonotonic
+
+ int BH_TimerIsMonotonic(BH_Timer *timer);
+
+Checks whether the timer uses a monotonic clock source.
+
+
+=head2 BH_TimerStart
+
+ void BH_TimerStart(BH_Timer *timer);
+
+Starts or resets the timer to begin counting from the current time.
+
+
+=head2 BH_TimerRestart
+
+ int64_t BH_TimerRestart(BH_Timer *timer);
+
+Restarts the timer and returns the number of milliseconds that had elapsed since
+the start of the timer.
+
+
+=head2 BH_TimerMilliseconds
+
+ int64_t BH_TimerMilliseconds(BH_Timer *timer);
+
+Returns the number of milliseconds that had elapsed since the start of the
+timer.
+
+
+=head2 BH_TimerNanoseconds
+
+ int64_t BH_TimerNanoseconds(BH_Timer *timer);
+
+Returns the number of nanoseconds that had elapsed since the start of the timer.
+
+=head1 SEE ALSO
+
+L<BH>
diff --git a/doc/Manual/en/Makefile b/doc/Manual/en/Makefile
index f5ff1f7..b2fd873 100644
--- a/doc/Manual/en/Makefile
+++ b/doc/Manual/en/Makefile
@@ -20,6 +20,7 @@ HTMLS = BH_Algo.html \
BH_Ray3f.html \
BH_String.html \
BH_Thread.html \
+ BH_Timer.html \
BH_Unicode.html \
BH_Util.html \
BH_Vec2f.html \
@@ -48,6 +49,7 @@ MANS = BH_Algo.3 \
BH_Ray3f.3 \
BH_String.3 \
BH_Thread.3 \
+ BH_Timer.3 \
BH_Unicode.3 \
BH_Util.3 \
BH_Vec2f.3 \
diff --git a/doc/Manual/ru/BH_Timer.pod b/doc/Manual/ru/BH_Timer.pod
new file mode 100644
index 0000000..290ec11
--- /dev/null
+++ b/doc/Manual/ru/BH_Timer.pod
@@ -0,0 +1,83 @@
+=encoding UTF-8
+
+
+=head1 НАЗВАНИЕ
+
+BH_Timer - утилиты таймера
+
+
+=head1 СИНТАКСИС
+
+ #include <BH/Timer.h>
+
+ cc prog.c -o prog -lbh
+
+
+=head1 ОПИСАНИЕ
+
+Этот модуль предоставляет простой интерфейс высокоточного таймера для измерения
+прошедшего времени. По возможности таймер использует монотонный источник часов,
+что делает его подходящим для точных измерений времени, например, профилирования
+или тайм-аутов. Поддерживается точность в миллисекундах и наносекундах; таймер
+можно запускать, перезапускать и запрашивать прошедшее время.
+
+
+=head1 API ВЫЗОВЫ
+
+
+=head2 BH_TimerNew
+
+ BH_Timer *BH_TimerNew(void);
+
+Создаёт и инициализирует новый объект таймера.
+
+В случае успеха функция возвращает указатель на новый объект BH_Timer,
+или NULL в случае ошибки.
+
+
+=head2 BH_TimerFree
+
+ void BH_TimerFree(BH_Timer *timer);
+
+Уничтожает таймер.
+
+
+=head2 BH_TimerIsMonotonic
+
+ int BH_TimerIsMonotonic(BH_Timer *timer);
+
+Проверяет, использует ли таймер монотонный источник времени.
+
+
+=head2 BH_TimerStart
+
+ void BH_TimerStart(BH_Timer *timer);
+
+Запускает или сбрасывает таймер, начиная отсчёт с текущего момента времени.
+
+
+=head2 BH_TimerRestart
+
+ int64_t BH_TimerRestart(BH_Timer *timer);
+
+Перезапускает таймер и возвращает количество миллисекунд, прошедших с момента
+его запуска.
+
+
+=head2 BH_TimerMilliseconds
+
+ int64_t BH_TimerMilliseconds(BH_Timer *timer);
+
+Возвращает количество миллисекунд, прошедших с момента запуска таймера.
+
+
+=head2 BH_TimerNanoseconds
+
+ int64_t BH_TimerNanoseconds(BH_Timer *timer);
+
+Возвращает количество наносекунд, прошедших с момента запуска таймера.
+
+
+=head1 СМ. ТАКЖЕ
+
+L<BH>
diff --git a/doc/Manual/ru/Makefile b/doc/Manual/ru/Makefile
index f5ff1f7..b2fd873 100644
--- a/doc/Manual/ru/Makefile
+++ b/doc/Manual/ru/Makefile
@@ -20,6 +20,7 @@ HTMLS = BH_Algo.html \
BH_Ray3f.html \
BH_String.html \
BH_Thread.html \
+ BH_Timer.html \
BH_Unicode.html \
BH_Util.html \
BH_Vec2f.html \
@@ -48,6 +49,7 @@ MANS = BH_Algo.3 \
BH_Ray3f.3 \
BH_String.3 \
BH_Thread.3 \
+ BH_Timer.3 \
BH_Unicode.3 \
BH_Util.3 \
BH_Vec2f.3 \
diff --git a/include/BH/Timer.h b/include/BH/Timer.h
new file mode 100644
index 0000000..51ab0de
--- /dev/null
+++ b/include/BH/Timer.h
@@ -0,0 +1,32 @@
+#ifndef BH_TIMER_H
+#define BH_TIMER_H
+
+
+#include "Common.h"
+
+
+typedef struct BH_Timer BH_Timer;
+
+
+BH_Timer *BH_TimerNew(void);
+
+
+void BH_TimerFree(BH_Timer *timer);
+
+
+int BH_TimerIsMonotonic(BH_Timer *timer);
+
+
+void BH_TimerStart(BH_Timer *timer);
+
+
+int64_t BH_TimerRestart(BH_Timer *timer);
+
+
+int64_t BH_TimerMilliseconds(BH_Timer *timer);
+
+
+int64_t BH_TimerNanoseconds(BH_Timer *timer);
+
+
+#endif /* BH_TIMER_H */
diff --git a/src/Platform/Dummy/Timer.c b/src/Platform/Dummy/Timer.c
new file mode 100644
index 0000000..af70274
--- /dev/null
+++ b/src/Platform/Dummy/Timer.c
@@ -0,0 +1,50 @@
+#include <BH/Timer.h>
+
+
+BH_Timer *BH_TimerNew(void)
+{
+ return NULL;
+}
+
+
+void BH_TimerFree(BH_Timer *timer)
+{
+ BH_UNUSED(timer);
+}
+
+
+int BH_TimerIsMonotonic(BH_Timer *timer)
+{
+ BH_UNUSED(timer);
+ return 0;
+}
+
+
+void BH_TimerStart(BH_Timer *timer)
+{
+ BH_UNUSED(timer);
+}
+
+
+int64_t BH_TimerRestart(BH_Timer *timer)
+{
+ BH_UNUSED(timer);
+
+ return 0;
+}
+
+
+int64_t BH_TimerMilliseconds(BH_Timer *timer)
+{
+ BH_UNUSED(timer);
+
+ return 0;
+}
+
+
+int64_t BH_TimerNanoseconds(BH_Timer *timer)
+{
+ BH_UNUSED(timer);
+
+ return 0;
+}
diff --git a/src/Platform/Posix/Timer.c b/src/Platform/Posix/Timer.c
new file mode 100644
index 0000000..0b68036
--- /dev/null
+++ b/src/Platform/Posix/Timer.c
@@ -0,0 +1,139 @@
+#include <BH/Timer.h>
+
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+
+#define TYPE_OLD_REALTIME 0x0000
+#define TYPE_NEW_REALTIME 0x0001
+#define TYPE_MONOTONIC 0x0002
+
+
+struct BH_Timer
+{
+ int type;
+ int64_t sec;
+ int64_t nsec;
+};
+
+
+static int checkAvailableTimer(void)
+{
+#if (_POSIX_TIMERS > 0) || defined(BH_USE_CLOCK_GETTIME)
+ struct timespec ts;
+
+ if (!clock_gettime(CLOCK_MONOTONIC, &ts))
+ return TYPE_MONOTONIC;
+
+ if (!clock_gettime(CLOCK_REALTIME, &ts))
+ return TYPE_NEW_REALTIME;
+#endif
+
+ return TYPE_OLD_REALTIME;
+}
+
+
+static void getTimer(int type,
+ int64_t *sec,
+ int64_t *nsec)
+{
+ struct timespec ts;
+ struct timeval tv;
+
+ switch (type)
+ {
+#if (_POSIX_TIMERS > 0) || defined(BH_USE_CLOCK_GETTIME)
+ case TYPE_MONOTONIC:
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ *sec = ts.tv_sec;
+ *nsec = ts.tv_nsec;
+ break;
+
+ case TYPE_NEW_REALTIME:
+ clock_gettime(CLOCK_REALTIME, &ts);
+ *sec = ts.tv_sec;
+ *nsec = ts.tv_nsec;
+ break;
+#endif
+
+ default:
+ case TYPE_OLD_REALTIME:
+ gettimeofday(&tv, NULL);
+ *sec = tv.tv_sec;
+ *nsec = tv.tv_usec * (uint64_t)1000;
+ break;
+ }
+}
+
+
+BH_Timer *BH_TimerNew(void)
+{
+ BH_Timer *result;
+
+ result = malloc(sizeof(*result));
+ if (result)
+ {
+ result->type = checkAvailableTimer();
+ BH_TimerStart(result);
+ }
+
+ return result;
+}
+
+
+void BH_TimerFree(BH_Timer *timer)
+{
+ free(timer);
+}
+
+
+int BH_TimerIsMonotonic(BH_Timer *timer)
+{
+ return timer->type == TYPE_MONOTONIC;
+}
+
+
+void BH_TimerStart(BH_Timer *timer)
+{
+ getTimer(timer->type, &timer->sec, &timer->nsec);
+}
+
+
+int64_t BH_TimerRestart(BH_Timer *timer)
+{
+ int64_t oldSec, oldNsec;
+
+ oldSec = timer->sec; oldNsec = timer->nsec;
+
+ getTimer(timer->type, &timer->sec, &timer->nsec);
+ oldSec = timer->sec - oldSec;
+ oldNsec = timer->nsec - oldNsec;
+
+ return (oldSec * 1000) + (oldNsec / 1000000);
+}
+
+
+int64_t BH_TimerMilliseconds(BH_Timer *timer)
+{
+ int64_t newSec, newNsec;
+
+ getTimer(timer->type, &newSec, &newNsec);
+ newSec -= timer->sec;
+ newNsec -= timer->nsec;
+
+ return (newSec * 1000) + (newNsec / 1000000);
+}
+
+
+int64_t BH_TimerNanoseconds(BH_Timer *timer)
+{
+ int64_t newSec, newNsec;
+
+ getTimer(timer->type, &newSec, &newNsec);
+ newSec -= timer->sec;
+ newNsec -= timer->nsec;
+
+ return (newSec * 1000000000) + newNsec;
+}
diff --git a/src/Platform/Win32/Timer.c b/src/Platform/Win32/Timer.c
new file mode 100644
index 0000000..03d4d7f
--- /dev/null
+++ b/src/Platform/Win32/Timer.c
@@ -0,0 +1,130 @@
+#include <BH/Timer.h>
+
+#include <stdlib.h>
+#include <windows.h>
+
+
+#define TYPE_TICKS 0x0000
+#define TYPE_COUNTER 0x0001
+
+
+struct BH_Timer
+{
+ int type;
+ LARGE_INTEGER frequency;
+ int64_t sec;
+ int64_t nsec;
+};
+
+
+static int checkAvailableTimer(LARGE_INTEGER *frequency)
+{
+ LARGE_INTEGER dummy;
+
+ if (QueryPerformanceFrequency(frequency))
+ {
+ if (QueryPerformanceCounter(&dummy))
+ {
+ return TYPE_COUNTER;
+ }
+ }
+
+ return TYPE_TICKS;
+}
+
+
+static void getTimer(int type,
+ LARGE_INTEGER *frequency,
+ int64_t *sec,
+ int64_t *nsec)
+{
+ LARGE_INTEGER newCount;
+ int64_t newTicks;
+
+ switch (type)
+ {
+ case TYPE_COUNTER:
+ QueryPerformanceCounter(&newCount);
+ *sec = newCount.QuadPart / frequency->QuadPart;
+ *nsec = (newCount.QuadPart - *sec * frequency->QuadPart) * 1000000000 / frequency->QuadPart;
+ break;
+
+ default:
+ case TYPE_TICKS:
+ newTicks = GetTickCount64();
+ *sec = newTicks / 1000;
+ *nsec = (newTicks - *sec * 1000) * 1000000;
+ break;
+ }
+}
+
+
+BH_Timer *BH_TimerNew(void)
+{
+ BH_Timer *result;
+
+ result = malloc(sizeof(*result));
+ if (result)
+ {
+ result->type = checkAvailableTimer(&result->frequency);
+ BH_TimerStart(result);
+ }
+
+ return result;
+}
+
+
+void BH_TimerFree(BH_Timer *timer)
+{
+ free(timer);
+}
+
+
+int BH_TimerIsMonotonic(BH_Timer *timer)
+{
+ return timer->type == TYPE_COUNTER;
+}
+
+
+void BH_TimerStart(BH_Timer *timer)
+{
+ getTimer(timer->type, &timer->frequency, &timer->sec, &timer->nsec);
+}
+
+
+int64_t BH_TimerRestart(BH_Timer *timer)
+{
+ int64_t oldSec, oldNsec;
+
+ oldSec = timer->sec; oldNsec = timer->nsec;
+
+ getTimer(timer->type, &timer->frequency, &timer->sec, &timer->nsec);
+ oldSec = timer->sec - oldSec;
+ oldNsec = timer->nsec - oldNsec;
+
+ return (oldSec * 1000) + (oldNsec / 1000000);
+}
+
+
+int64_t BH_TimerMilliseconds(BH_Timer *timer)
+{
+ int64_t newSec, newNsec;
+
+ getTimer(timer->type, &timer->frequency, &newSec, &newNsec);
+ newSec -= timer->sec;
+ newNsec -= timer->nsec;
+
+ return (newSec * 1000) + (newNsec / 1000000);
+}
+
+
+int64_t BH_TimerNanoseconds(BH_Timer *timer)
+{
+ int64_t newSec, newNsec;
+
+ getTimer(timer->type, &timer->frequency, &newSec, &newNsec);
+ newSec -= timer->sec;
+ newNsec -= timer->nsec;
+
+ return (newSec * 1000000000) + newNsec;
+}
diff --git a/test/src/TestTimer.c b/test/src/TestTimer.c
new file mode 100644
index 0000000..b08dba8
--- /dev/null
+++ b/test/src/TestTimer.c
@@ -0,0 +1,36 @@
+#include <BH/Unit.h>
+#include <BH/Timer.h>
+#include <BH/Thread.h>
+
+
+BH_UNIT_TEST(General)
+{
+ BH_Timer *timer;
+
+ BH_VERIFY((timer = BH_TimerNew()) != NULL);
+ BH_ThreadSleep(5000);
+
+ BH_VERIFY(BH_TimerMilliseconds(timer) >= 5000);
+ BH_VERIFY(BH_TimerNanoseconds(timer) >= 5000000000);
+ BH_VERIFY(BH_TimerRestart(timer) >= 5000);
+ BH_VERIFY(BH_TimerRestart(timer) < 5000);
+
+ BH_ThreadSleep(5000);
+ BH_VERIFY(BH_TimerMilliseconds(timer) >= 5000);
+ BH_TimerStart(timer);
+ BH_VERIFY(BH_TimerMilliseconds(timer) < 5000);
+
+ BH_TimerFree(timer);
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+
+ BH_UNIT_ADD(General);
+
+ return BH_UnitRun();
+}