diff --git a/doc/xml/release.xml b/doc/xml/release.xml index d7406be3e..29c87f491 100644 --- a/doc/xml/release.xml +++ b/doc/xml/release.xml @@ -54,7 +54,11 @@ -

Add Buffer, Ini, KeyValue, List, RegExp, Storage, String, StringList, Variant, and VariantList objects.

+

Add Buffer, Ini, KeyValue, List, RegExp, Storage, String, StringList, Variant, VariantList, and Wait objects.

+
+ + +

Add time module.

diff --git a/src/Makefile b/src/Makefile index 4a49cf24b..10e6e939d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,6 +8,7 @@ pgbackrest: \ common/ini.o \ common/memContext.o \ common/regExp.o \ + common/time.o \ common/type/buffer.o \ common/type/keyValue.o \ common/type/list.o \ @@ -15,6 +16,7 @@ pgbackrest: \ common/type/stringList.o \ common/type/variant.o \ common/type/variantList.o \ + common/wait.o \ config/config.o \ config/define.o \ config/parse.o \ @@ -27,6 +29,7 @@ pgbackrest: \ common/ini.o \ common/memContext.o \ common/regExp.o \ + common/time.o \ common/type/buffer.o \ common/type/keyValue.o \ common/type/list.o \ @@ -34,6 +37,7 @@ pgbackrest: \ common/type/stringList.o \ common/type/variant.o \ common/type/variantList.o \ + common/wait.o \ config/config.o \ config/define.o \ config/parse.o \ diff --git a/src/common/time.c b/src/common/time.c new file mode 100644 index 000000000..83eccd7b0 --- /dev/null +++ b/src/common/time.c @@ -0,0 +1,30 @@ +/*********************************************************************************************************************************** +Time Management +***********************************************************************************************************************************/ +#include + +#include "common/time.h" +#include "common/type.h" + +/*********************************************************************************************************************************** +Epoch time in microseconds +***********************************************************************************************************************************/ +TimeUSec +timeUSec() +{ + struct timeval currentTime; + gettimeofday(¤tTime, NULL); + return ((TimeUSec)currentTime.tv_sec * USEC_PER_SEC) + (TimeUSec)currentTime.tv_usec; +} + +/*********************************************************************************************************************************** +Sleep for specified microseconds +***********************************************************************************************************************************/ +void +sleepUSec(TimeUSec sleepUSec) +{ + struct timeval delay; + delay.tv_sec = sleepUSec / USEC_PER_SEC; + delay.tv_usec = sleepUSec % USEC_PER_SEC; + select(0, NULL, NULL, NULL, &delay); +} diff --git a/src/common/time.h b/src/common/time.h new file mode 100644 index 000000000..e6c0cef00 --- /dev/null +++ b/src/common/time.h @@ -0,0 +1,25 @@ +/*********************************************************************************************************************************** +Time Management +***********************************************************************************************************************************/ +#ifndef COMMON_TIME_H +#define COMMON_TIME_H + +#include "common/type.h" + +/*********************************************************************************************************************************** +Time types +***********************************************************************************************************************************/ +typedef uint64 TimeUSec; + +/*********************************************************************************************************************************** +Constants describing number if sub-units in a second +***********************************************************************************************************************************/ +#define USEC_PER_SEC ((TimeUSec)1000000) + +/*********************************************************************************************************************************** +Functions +***********************************************************************************************************************************/ +void sleepUSec(TimeUSec); +TimeUSec timeUSec(); + +#endif diff --git a/src/common/wait.c b/src/common/wait.c new file mode 100644 index 000000000..d777dabdf --- /dev/null +++ b/src/common/wait.c @@ -0,0 +1,106 @@ +/*********************************************************************************************************************************** +Wait Handler +***********************************************************************************************************************************/ +#include "common/memContext.h" +#include "common/time.h" +#include "common/wait.h" + +/*********************************************************************************************************************************** +Contains information about the wait handler +***********************************************************************************************************************************/ +struct Wait +{ + MemContext *memContext; // Context that contains the wait handler + TimeUSec waitTime; // Total time to wait (in usec) + TimeUSec sleepTime; // Next sleep time (in usec) + TimeUSec sleepPrevTime; // Previous time slept (in usec) + TimeUSec beginTime; // Time the wait began (in epoch usec) +}; + +/*********************************************************************************************************************************** +New wait handler +***********************************************************************************************************************************/ +Wait * +waitNew(double waitTime) +{ + // Make sure wait time is valid + if (waitTime < 0.1 || waitTime > 999999.0) + THROW(AssertError, "waitTime must be >= 0.1 and <= 999999.0"); + + // Allocate wait object + Wait *this = NULL; + + MEM_CONTEXT_NEW_BEGIN("wait") + { + // Create object + this = memNew(sizeof(Wait)); + this->memContext = MEM_CONTEXT_NEW(); + + // Store time + this->waitTime = (TimeUSec)(waitTime * USEC_PER_SEC); + + // Calculate first sleep time -- start with 1/10th of a second for anything >= 1 second + if (this->waitTime >= USEC_PER_SEC) + this->sleepTime = USEC_PER_SEC / 10; + // Unless the wait time is really small -- in that case divide wait time by 10 + else + this->sleepTime = this->waitTime / 10; + + // Get beginning time + this->beginTime = timeUSec(); + } + MEM_CONTEXT_NEW_END(); + + return this; +} + +/*********************************************************************************************************************************** +Wait and return whether the caller has more time left +***********************************************************************************************************************************/ +bool +waitMore(Wait *this) +{ + bool result = false; + + // If sleep is 0 then the wait time has already ended + if (this->sleepTime > 0) + { + // Sleep required amount + sleepUSec(this->sleepTime); + + // Get the end time + TimeUSec elapsedTime = timeUSec() - this->beginTime; + + // Is there more time to go? + if (elapsedTime < this->waitTime) + { + // Calculate sleep time as a sum of current and last (a Fibonacci-type sequence) + TimeUSec sleepNextTime = this->sleepTime + this->sleepPrevTime; + + // Make sure sleep time does not go beyond end time (this won't be negative because of the if condition above) + if (sleepNextTime > this->waitTime - elapsedTime) + sleepNextTime = this->waitTime - elapsedTime; + + // Store new sleep times + this->sleepPrevTime = this->sleepTime; + this->sleepTime = sleepNextTime; + } + // Else set sleep to zero so next call will return false + else + this->sleepTime = 0; + + // Need to wait more + result = true; + } + + return result; +} + +/*********************************************************************************************************************************** +Free the wait +***********************************************************************************************************************************/ +void +waitFree(Wait *this) +{ + memContextFree(this->memContext); +} diff --git a/src/common/wait.h b/src/common/wait.h new file mode 100644 index 000000000..ba010f0ed --- /dev/null +++ b/src/common/wait.h @@ -0,0 +1,19 @@ +/*********************************************************************************************************************************** +Wait Handler +***********************************************************************************************************************************/ +#ifndef COMMON_WAIT_H +#define COMMON_WAIT_H + +/*********************************************************************************************************************************** +Ini object +***********************************************************************************************************************************/ +typedef struct Wait Wait; + +/*********************************************************************************************************************************** +Functions +***********************************************************************************************************************************/ +Wait *waitNew(double waitTime); +bool waitMore(Wait *this); +void waitFree(Wait *this); + +#endif diff --git a/test/lib/pgBackRestTest/Common/DefineTest.pm b/test/lib/pgBackRestTest/Common/DefineTest.pm index 7072abcbc..eace0d5a5 100644 --- a/test/lib/pgBackRestTest/Common/DefineTest.pm +++ b/test/lib/pgBackRestTest/Common/DefineTest.pm @@ -131,6 +131,26 @@ my $oTestDef = 'common/memContext' => TESTDEF_COVERAGE_FULL, }, }, + { + &TESTDEF_NAME => 'time', + &TESTDEF_TOTAL => 2, + &TESTDEF_C => true, + + &TESTDEF_COVERAGE => + { + 'common/time' => TESTDEF_COVERAGE_FULL, + }, + }, + { + &TESTDEF_NAME => 'wait', + &TESTDEF_TOTAL => 1, + &TESTDEF_C => true, + + &TESTDEF_COVERAGE => + { + 'common/wait' => TESTDEF_COVERAGE_FULL, + }, + }, { &TESTDEF_NAME => 'type-list', &TESTDEF_TOTAL => 2, diff --git a/test/src/module/common/timeTest.c b/test/src/module/common/timeTest.c new file mode 100644 index 000000000..566d65c75 --- /dev/null +++ b/test/src/module/common/timeTest.c @@ -0,0 +1,30 @@ +/*********************************************************************************************************************************** +Test Time Management +***********************************************************************************************************************************/ + +/*********************************************************************************************************************************** +Test Run +***********************************************************************************************************************************/ +void testRun() +{ + // ***************************************************************************************************************************** + if (testBegin("timeUSec()")) + { + // Make sure the time returned is between 2017 and 2100 + TEST_RESULT_BOOL(timeUSec() > (TimeUSec)1483228800000000, true, "lower range check"); + TEST_RESULT_BOOL(timeUSec() < (TimeUSec)4102444800000000, true, "upper range check"); + } + + // ***************************************************************************************************************************** + if (testBegin("sleepUSec()")) + { + // Sleep and measure time slept + TimeUSec begin = timeUSec(); + sleepUSec(1400000); + TimeUSec end = timeUSec(); + + // Check bounds for time slept (within a range of .1 seconds) + TEST_RESULT_BOOL(end - begin > (TimeUSec)1400000, true, "lower range check"); + TEST_RESULT_BOOL(end - begin < (TimeUSec)1500000, true, "upper range check"); + } +} diff --git a/test/src/module/common/waitTest.c b/test/src/module/common/waitTest.c new file mode 100644 index 000000000..756c68fe4 --- /dev/null +++ b/test/src/module/common/waitTest.c @@ -0,0 +1,54 @@ +/*********************************************************************************************************************************** +Test Wait Handler +***********************************************************************************************************************************/ + +/*********************************************************************************************************************************** +Test Run +***********************************************************************************************************************************/ +void testRun() +{ + // ***************************************************************************************************************************** + if (testBegin("waitNew(), waitMore, and waitFree()")) + { + Wait *wait = NULL; + + TEST_ERROR(waitNew(0.01), AssertError, "waitTime must be >= 0.1 and <= 999999.0"); + TEST_ERROR(waitNew(9999999), AssertError, "waitTime must be >= 0.1 and <= 999999.0"); + + // ------------------------------------------------------------------------------------------------------------------------- + unsigned long begin = timeUSec(); + + TEST_ASSIGN(wait, waitNew(0.2), "new wait = 0.2 sec"); + TEST_RESULT_DOUBLE(wait->waitTime, 200000, " check wait time"); + TEST_RESULT_DOUBLE(wait->sleepTime, 20000, " check sleep time"); + TEST_RESULT_DOUBLE(wait->sleepPrevTime, 0, " check sleep prev time"); + TEST_RESULT_BOOL(wait->beginTime > (unsigned long)1483228800000000, true, " check begin time"); + + while (waitMore(wait)); + unsigned long end = timeUSec(); + + // Check bounds for time slept (within a range of .1 seconds) + TEST_RESULT_BOOL(end - begin >= wait->waitTime, true, " lower range check"); + TEST_RESULT_BOOL(end - begin < wait->waitTime + 100000, true, " upper range check"); + + TEST_RESULT_VOID(waitFree(wait), " free wait"); + + // ------------------------------------------------------------------------------------------------------------------------- + begin = timeUSec(); + + TEST_ASSIGN(wait, waitNew(1.1), "new wait = 1.1 sec"); + TEST_RESULT_DOUBLE(wait->waitTime, 1100000, " check wait time"); + TEST_RESULT_DOUBLE(wait->sleepTime, 100000, " check sleep time"); + TEST_RESULT_DOUBLE(wait->sleepPrevTime, 0, " check sleep prev time"); + TEST_RESULT_BOOL(wait->beginTime > (unsigned long)1483228800000000, true, " check begin time"); + + while (waitMore(wait)); + end = timeUSec(); + + // Check bounds for time slept (within a range of .1 seconds) + TEST_RESULT_BOOL(end - begin >= wait->waitTime, true, " lower range check"); + TEST_RESULT_BOOL(end - begin < wait->waitTime + 1200000, true, " upper range check"); + + TEST_RESULT_VOID(waitFree(wait), " free wait"); + } +}