Browse Source

Merge branch 'monotonic_timers' into bfgminer

Luke Dashjr 12 years ago
parent
commit
b1657ed18d
17 changed files with 343 additions and 130 deletions
  1. 3 3
      api.c
  2. 4 1
      compat.h
  3. 29 0
      configure.ac
  4. 12 13
      deviceapi.c
  5. 1 3
      driver-avalon.c
  6. 1 3
      driver-bitforce.c
  7. 2 2
      driver-cpu.c
  8. 1 4
      driver-icarus.c
  9. 8 9
      driver-modminer.c
  10. 3 7
      driver-opencl.c
  11. 7 6
      driver-x6500.c
  12. 1 3
      driver-ztex.c
  13. 19 23
      logging.c
  14. 50 36
      miner.c
  15. 6 2
      miner.h
  16. 136 8
      util.c
  17. 60 7
      util.h

+ 3 - 3
api.c

@@ -3565,7 +3565,7 @@ void api(int api_thr_id)
 	int n, bound;
 	char *connectaddr;
 	const char *binderror;
-	time_t bindstart;
+	struct timeval bindstart;
 	short int port = opt_api_port;
 	struct sockaddr_in serv;
 	struct sockaddr_in cli;
@@ -3650,11 +3650,11 @@ void api(int api_thr_id)
 
 	// try for more than 1 minute ... in case the old one hasn't completely gone yet
 	bound = 0;
-	bindstart = time(NULL);
+	cgtime(&bindstart);
 	while (bound == 0) {
 		if (SOCKETFAIL(bind(*apisock, (struct sockaddr *)(&serv), sizeof(serv)))) {
 			binderror = SOCKERRMSG;
-			if ((time(NULL) - bindstart) > 61)
+			if (timer_elapsed(&bindstart, NULL) > 61)
 				break;
 			else {
 				applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port);

+ 4 - 1
compat.h

@@ -85,7 +85,8 @@ struct tm *localtime_convert(time_t t)
 #endif
 
 #ifndef HAVE_NANOSLEEP
-extern void cgtime(struct timeval *);
+extern void (*timer_set_now)(struct timeval *);
+#define cgtime(tvp)  timer_set_now(tvp)
 
 static inline int nanosleep(const struct timespec *req, struct timespec *rem)
 {
@@ -118,6 +119,8 @@ static inline int nanosleep(const struct timespec *req, struct timespec *rem)
 	}
 	return 0;
 }
+
+#undef cgtime
 #endif
 
 #ifdef WIN32

+ 29 - 0
configure.ac

@@ -844,6 +844,35 @@ AC_TRY_COMPILE([
 ])
 
 
+AC_MSG_CHECKING([for clock_gettime(CLOCK_MONOTONIC)])
+AC_TRY_COMPILE([
+	#define _GNU_SOURCE
+	#include <time.h>
+],[
+	struct timespec ts;
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+],[
+	AC_MSG_RESULT([yes])
+	AC_DEFINE([HAVE_CLOCK_GETTIME_MONOTONIC], [1], [Defined to 1 if clock_gettime(CLOCK_MONOTONIC) is defined])
+	AC_SEARCH_LIBS([clock_gettime],[rt posix4])
+	AC_MSG_CHECKING([for clock_gettime(CLOCK_MONOTONIC_RAW)])
+	AC_TRY_COMPILE([
+		#define _GNU_SOURCE
+		#include <time.h>
+	],[
+		struct timespec ts;
+		clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+	],[
+		AC_MSG_RESULT([yes])
+		AC_DEFINE([HAVE_CLOCK_GETTIME_MONOTONIC_RAW], [1], [Defined to 1 if clock_gettime(CLOCK_MONOTONIC_RAW) is defined])
+	],[
+		AC_MSG_RESULT([no])
+	])
+],[
+	AC_MSG_RESULT([no])
+])
+
+
 if test "x$prefix" = xNONE; then
 	prefix=/usr/local
 fi

+ 12 - 13
deviceapi.c

@@ -37,8 +37,7 @@ bool hashes_done(struct thr_info *thr, int64_t hashes, struct timeval *tvp_hashe
 	const long cycle = opt_log_interval / 5 ? : 1;
 	
 	if (unlikely(hashes == -1)) {
-		time_t now = time(NULL);
-		if (difftime(now, cgpu->device_last_not_well) > 1.)
+		if (timer_elapsed(&cgpu->tv_device_last_not_well, NULL) > 0)
 			dev_error(cgpu, REASON_THREAD_ZERO_HASH);
 		
 		if (thr->scanhash_working && opt_restart) {
@@ -103,7 +102,7 @@ int restart_wait(struct thr_info *thr, unsigned int mstime)
 		return (thr->work_restart ? 0 : ETIMEDOUT);
 	}
 	
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	timer_set_delay(&tv_timer, &tv_now, mstime * 1000);
 	while (true)
 	{
@@ -119,7 +118,7 @@ int restart_wait(struct thr_info *thr, unsigned int mstime)
 				return 0;
 			notifier_read(thr->work_restart_notifier);
 		}
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 	}
 }
 
@@ -164,16 +163,16 @@ void minerloop_scanhash(struct thr_info *mythr)
 		work = get_and_prepare_work(mythr);
 		if (!work)
 			break;
-		gettimeofday(&(work->tv_work_start), NULL);
+		timer_set_now(&work->tv_work_start);
 		
 		do {
 			thread_reportin(mythr);
 			/* Only allow the mining thread to be cancelled when
 			* it is not in the driver code. */
 			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
-			gettimeofday(&tv_start, NULL);
+			timer_set_now(&tv_start);
 			hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce);
-			gettimeofday(&tv_end, NULL);
+			timer_set_now(&tv_end);
 			pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
 			pthread_testcancel();
 			thread_reportin(mythr);
@@ -277,7 +276,7 @@ void job_results_fetched(struct thr_info *mythr)
 	{
 		struct timeval tv_now;
 		
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 		
 		do_process_results(mythr, &tv_now, mythr->prev_work, true);
 	}
@@ -296,7 +295,7 @@ void mt_job_transition(struct thr_info *mythr)
 {
 	struct timeval tv_now;
 	
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	
 	if (mythr->starting_next_work)
 	{
@@ -315,7 +314,7 @@ void job_start_complete(struct thr_info *mythr)
 {
 	struct timeval tv_now;
 	
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	
 	do_process_results(mythr, &tv_now, mythr->prev_work, false);
 }
@@ -359,7 +358,7 @@ void do_notifier_select(struct thr_info *thr, struct timeval *tvp_timeout)
 	int maxfd;
 	fd_set rfds;
 	
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	FD_ZERO(&rfds);
 	FD_SET(thr->notifier[0], &rfds);
 	maxfd = thr->notifier[0];
@@ -404,7 +403,7 @@ void minerloop_async(struct thr_info *mythr)
 	
 	while (likely(!cgpu->shutdown)) {
 		tv_timeout.tv_sec = -1;
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 		for (proc = cgpu; proc; proc = proc->next_proc)
 		{
 			mythr = proc->thr[0];
@@ -489,7 +488,7 @@ void minerloop_queue(struct thr_info *thr)
 	
 	while (likely(!cgpu->shutdown)) {
 		tv_timeout.tv_sec = -1;
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 		for (proc = cgpu; proc; proc = proc->next_proc)
 		{
 			mythr = proc->thr[0];

+ 1 - 3
driver-avalon.c

@@ -662,7 +662,6 @@ static bool avalon_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *avalon = thr->cgpu;
 	struct avalon_info *info = avalon->device_data;
-	struct timeval now;
 
 	free(avalon->works);
 	avalon->works = calloc(info->miner_count * sizeof(struct work *),
@@ -674,8 +673,7 @@ static bool avalon_prepare(struct thr_info *thr)
 	else
 		__avalon_init(avalon);
 
-	cgtime(&now);
-	get_datestamp(avalon->init, &now);
+	get_now_datestamp(avalon->init);
 	avalon->status = LIFE_INIT2;
 	return true;
 }

+ 1 - 3
driver-bitforce.c

@@ -356,7 +356,6 @@ static bool bitforce_thread_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 	int fdDev = BFopen(bitforce->device_path);
-	struct timeval now;
 
 	if (unlikely(fdDev == -1)) {
 		applog(LOG_ERR, "%s: Failed to open %s", bitforce->dev_repr, bitforce->device_path);
@@ -366,8 +365,7 @@ static bool bitforce_thread_prepare(struct thr_info *thr)
 	bitforce->device_fd = fdDev;
 
 	applog(LOG_INFO, "%s: Opened %s", bitforce->dev_repr, bitforce->device_path);
-	cgtime(&now);
-	get_datestamp(bitforce->init, &now);
+	get_now_datestamp(bitforce->init);
 
 	return true;
 }

+ 2 - 2
driver-cpu.c

@@ -246,7 +246,7 @@ double bench_algo_stage3(
 
 	memcpy(&hash1[0], &hash1_init[0], sizeof(hash1));
 
-	gettimeofday(&start, 0);
+	timer_set_now(&start);
 			{
 				sha256_func func = sha256_funcs[algo];
 				(*func)(
@@ -261,7 +261,7 @@ double bench_algo_stage3(
 					work.blk.nonce
 				);
 			}
-	gettimeofday(&end, 0);
+	timer_set_now(&end);
 
 	uint64_t usec_end = ((uint64_t)end.tv_sec)*1000*1000 + end.tv_usec;
 	uint64_t usec_start = ((uint64_t)start.tv_sec)*1000*1000 + start.tv_usec;

+ 1 - 4
driver-icarus.c

@@ -645,8 +645,6 @@ static bool icarus_prepare(struct thr_info *thr)
 	struct cgpu_info *icarus = thr->cgpu;
 	struct ICARUS_INFO *info = icarus->device_data;
 
-	struct timeval now;
-
 	icarus->device_fd = -1;
 
 	int fd = icarus_open2(icarus->device_path, info->baud, true);
@@ -659,8 +657,7 @@ static bool icarus_prepare(struct thr_info *thr)
 	icarus->device_fd = fd;
 
 	applog(LOG_INFO, "Opened Icarus on %s", icarus->device_path);
-	cgtime(&now);
-	get_datestamp(icarus->init, &now);
+	get_now_datestamp(icarus->init);
 
 	struct icarus_state *state;
 	thr->cgpu_data = state = calloc(1, sizeof(*state));

+ 8 - 9
driver-modminer.c

@@ -64,7 +64,7 @@ struct modminer_fpga_state {
 	// Number of nonces did meet pdiff 1, ever
 	int good_share_counter;
 	// Time the clock was last reduced due to temperature
-	time_t last_cutoff_reduced;
+	struct timeval tv_last_cutoff_reduced;
 
 	unsigned char temp;
 
@@ -276,9 +276,7 @@ modminer_device_prepare(struct cgpu_info *modminer)
 	modminer->device->device_fd = fd;
 	applog(LOG_INFO, "%s: Opened %s", modminer->dev_repr, modminer->device_path);
 
-	struct timeval now;
-	gettimeofday(&now, NULL);
-	get_datestamp(modminer->init, &now);
+	get_now_datestamp(modminer->init);
 
 	return true;
 }
@@ -486,9 +484,10 @@ static void modminer_get_temperature(struct cgpu_info *modminer, struct thr_info
 		state->temp = temperature;
 		if (temperature > modminer->targettemp + opt_hysteresis) {
 			{
-				time_t now = time(NULL);
-				if (state->last_cutoff_reduced != now) {
-					state->last_cutoff_reduced = now;
+				struct timeval now;
+				cgtime(&now);
+				if (timer_elapsed(&state->tv_last_cutoff_reduced, &now)) {
+					state->tv_last_cutoff_reduced = now;
 					int oldFreq = state->dclk.freqM;
 					if (modminer_reduce_clock(thr, false))
 						applog(LOG_NOTICE, "%s: Frequency %s from %u to %u MHz (temp: %d)",
@@ -593,7 +592,7 @@ fd_set fds;
 
 	if (46 != write(fd, state->next_work_cmd, 46))
 		bailout2(LOG_ERR, "%s: Error writing (start work)", modminer->proc_repr);
-	gettimeofday(&state->tv_workstart, NULL);
+	timer_set_now(&state->tv_workstart);
 	state->hashes = 0;
 	status_read("start work");
 	mutex_unlock(mutexp);
@@ -674,7 +673,7 @@ modminer_process_results(struct thr_info*thr)
 	}
 
 	struct timeval tv_workend, elapsed;
-	gettimeofday(&tv_workend, NULL);
+	timer_set_now(&tv_workend);
 	timersub(&tv_workend, &state->tv_workstart, &elapsed);
 
 	uint64_t hashes = (uint64_t)state->dclk.freqM * 2 * (((uint64_t)elapsed.tv_sec * 1000000) + elapsed.tv_usec);

+ 3 - 7
driver-opencl.c

@@ -888,7 +888,7 @@ void opencl_wlogprint_status(struct cgpu_info *cgpu)
 		if (thr->cgpu != cgpu)
 			continue;
 		
-		get_datestamp(checkin, &thr->last);
+		get_datestamp(checkin, time(NULL) - timer_elapsed(&thr->last, NULL));
 		displayed_rolling = thr->rolling;
 		if (!mhash_base)
 			displayed_rolling *= 1000;
@@ -1251,7 +1251,6 @@ void *reinit_gpu(void *userdata)
 	struct thr_info *mythr = userdata;
 	struct cgpu_info *cgpu, *sel_cgpu;
 	struct thr_info *thr;
-	struct timeval now;
 	char name[256];
 	int thr_id;
 	int i;
@@ -1317,8 +1316,7 @@ select_cgpu:
 		applog(LOG_WARNING, "Thread %d restarted", thr_id);
 	}
 
-	cgtime(&now);
-	get_datestamp(sel_cgpu->init, &now);
+	get_now_datestamp(sel_cgpu->init);
 
 	proc_enable(cgpu);
 
@@ -1500,7 +1498,6 @@ static uint32_t *blank_res;
 static bool opencl_thread_prepare(struct thr_info *thr)
 {
 	char name[256];
-	struct timeval now;
 	struct cgpu_info *cgpu = thr->cgpu;
 	int gpu = cgpu->device_id;
 	int virtual_gpu = cgpu->virtual_gpu;
@@ -1571,8 +1568,7 @@ static bool opencl_thread_prepare(struct thr_info *thr)
 		}
 	}
 	applog(LOG_INFO, "initCl() finished. Found %s", name);
-	cgtime(&now);
-	get_datestamp(cgpu->init, &now);
+	get_now_datestamp(cgpu->init);
 
 	have_opencl = true;
 

+ 7 - 6
driver-x6500.c

@@ -202,7 +202,7 @@ struct x6500_fpga_data {
 	uint8_t freqMaxMaxM;
 
 	// Time the clock was last reduced due to temperature
-	time_t last_cutoff_reduced;
+	struct timeval tv_last_cutoff_reduced;
 
 	uint32_t prepwork_last_register;
 };
@@ -499,9 +499,10 @@ void x6500_get_temperature(struct cgpu_info *x6500)
 
 		int temperature = round(x6500->temp);
 		if (temperature > x6500->targettemp + opt_hysteresis) {
-			time_t now = time(NULL);
-			if (fpga->last_cutoff_reduced != now) {
-				fpga->last_cutoff_reduced = now;
+			struct timeval now;
+			cgtime(&now);
+			if (timer_elapsed(&fpga->tv_last_cutoff_reduced, &now)) {
+				fpga->tv_last_cutoff_reduced = now;
 				int oldFreq = fpga->dclk.freqM;
 				if (x6500_change_clock(thr, oldFreq - 1))
 					applog(LOG_NOTICE, "%"PRIprepr": Frequency dropped from %u to %u MHz (temp: %.1fC)",
@@ -623,7 +624,7 @@ void x6500_job_start(struct thr_info *thr)
 
 	ft232r_flush(jp->a->ftdi);
 
-	gettimeofday(&tv_now, NULL);
+	timer_set_now(&tv_now);
 	if (!thr->prev_work)
 		fpga->tv_hashstart = tv_now;
 	else
@@ -679,7 +680,7 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 	bool bad;
 
 	while (1) {
-		gettimeofday(&tv_now, NULL);
+		timer_set_now(&tv_now);
 		nonce = x6500_get_register(jtag, 0xE);
 		if (nonce != 0xffffffff) {
 			bad = !(work && test_nonce(work, nonce, false));

+ 1 - 3
driver-ztex.c

@@ -339,12 +339,10 @@ get_ztex_drv_extra_device_status(struct cgpu_info *ztex)
 
 static bool ztex_prepare(struct thr_info *thr)
 {
-	struct timeval now;
 	struct cgpu_info *cgpu = thr->cgpu;
 	struct libztex_device *ztex = cgpu->device_ztex;
 
-	cgtime(&now);
-	get_datestamp(cgpu->init, &now);
+	get_now_datestamp(cgpu->init);
 	
 	{
 		char *fpganame = malloc(LIBZTEX_SNSTRING_LEN+3+1);

+ 19 - 23
logging.c

@@ -41,7 +41,7 @@ static void my_log_curses(int prio, const char *datetime, const char *str)
 		bool scs;
 		scs = !pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancelstate);
 		mutex_lock(&console_lock);
-		printf("%s%s%s", datetime, str, "                    \n");
+		printf(" %s %s%s", datetime, str, "                    \n");
 		mutex_unlock(&console_lock);
 		if (scs)
 			pthread_setcancelstate(cancelstate, &cancelstate);
@@ -69,35 +69,31 @@ void _applog(int prio, const char *str)
 			return;
 
 		char datetime[64];
-		struct timeval tv = {0, 0};
-		struct tm _tm;
-		struct tm *tm = &_tm;
-
-		cgtime(&tv);
-
-		localtime_r(&tv.tv_sec, tm);
 
 		if (opt_log_microseconds)
-			sprintf(datetime, " [%d-%02d-%02d %02d:%02d:%02d.%06ld] ",
-				tm->tm_year + 1900,
-				tm->tm_mon + 1,
-				tm->tm_mday,
-				tm->tm_hour,
-				tm->tm_min,
-				tm->tm_sec,
+		{
+			struct timeval tv;
+			struct tm tm;
+			
+			bfg_init_time();
+			bfg_gettimeofday(&tv);
+			localtime_r(&tv.tv_sec, &tm);
+			
+			sprintf(datetime, "[%d-%02d-%02d %02d:%02d:%02d.%06ld]",
+				tm.tm_year + 1900,
+				tm.tm_mon + 1,
+				tm.tm_mday,
+				tm.tm_hour,
+				tm.tm_min,
+				tm.tm_sec,
 				(long)tv.tv_usec);
+		}
 		else
-			sprintf(datetime, " [%d-%02d-%02d %02d:%02d:%02d] ",
-				tm->tm_year + 1900,
-				tm->tm_mon + 1,
-				tm->tm_mday,
-				tm->tm_hour,
-				tm->tm_min,
-				tm->tm_sec);
+			get_now_datestamp(datetime);
 
 		/* Only output to stderr if it's not going to the screen as well */
 		if (writetofile) {
-			fprintf(stderr, "%s%s\n", datetime, str);	/* atomic write to stderr */
+			fprintf(stderr, " %s %s\n", datetime, str);	/* atomic write to stderr */
 			fflush(stderr);
 		}
 

+ 50 - 36
miner.c

@@ -321,7 +321,6 @@ struct stratum_share {
 	bool block;
 	struct work *work;
 	int id;
-	time_t sshare_time;
 };
 
 static struct stratum_share *stratum_shares = NULL;
@@ -370,15 +369,15 @@ static bool time_before(struct tm *tm1, struct tm *tm2)
 
 static bool should_run(void)
 {
-	struct timeval tv;
 	struct tm tm;
+	time_t tt;
 	bool within_range;
 
 	if (!schedstart.enable && !schedstop.enable)
 		return true;
 
-	cgtime(&tv);
-	localtime_r(&tv.tv_sec, &tm);
+	tt = time(NULL);
+	localtime_r(&tt, &tm);
 
 	// NOTE: This is delicately balanced so that should_run is always false if schedstart==schedstop
 	if (time_before(&schedstop.tm, &schedstart.tm))
@@ -393,12 +392,15 @@ static bool should_run(void)
 	return within_range;
 }
 
-void get_datestamp(char *f, struct timeval *tv)
+void get_datestamp(char *f, time_t tt)
 {
 	struct tm _tm;
 	struct tm *tm = &_tm;
+	
+	if (tt == INVALID_TIMESTAMP)
+		tt = time(NULL);
 
-	localtime_r(&tv->tv_sec, tm);
+	localtime_r(&tt, tm);
 	sprintf(f, "[%d-%02d-%02d %02d:%02d:%02d]",
 		tm->tm_year + 1900,
 		tm->tm_mon + 1,
@@ -408,12 +410,12 @@ void get_datestamp(char *f, struct timeval *tv)
 		tm->tm_sec);
 }
 
-void get_timestamp(char *f, struct timeval *tv)
+void get_timestamp(char *f, time_t tt)
 {
 	struct tm _tm;
 	struct tm *tm = &_tm;
 
-	localtime_r(&tv->tv_sec, tm);
+	localtime_r(&tt, tm);
 	sprintf(f, "[%02d:%02d:%02d]",
 		tm->tm_hour,
 		tm->tm_min,
@@ -525,11 +527,12 @@ struct pool *add_pool(void)
 		quit(1, "Failed to pthread_cond_init in add_pool");
 	cglock_init(&pool->data_lock);
 	mutex_init(&pool->stratum_lock);
-	pool->swork.transparency_time = (time_t)-1;
+	timer_unset(&pool->swork.tv_transparency);
 
 	/* Make sure the pool doesn't think we've been idle since time 0 */
 	pool->tv_idle.tv_sec = ~0UL;
 	
+	bfg_init_time();
 	cgtime(&pool->cgminer_stats.start_tv);
 
 	pool->rpc_proxy = NULL;
@@ -1994,12 +1997,14 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 	}
 	
 	if (work->tmpl) {
-		const char *err = blktmpl_add_jansson(work->tmpl, res_val, time(NULL));
+		struct timeval tv_now;
+		cgtime(&tv_now);
+		const char *err = blktmpl_add_jansson(work->tmpl, res_val, tv_now.tv_sec);
 		if (err) {
 			applog(LOG_ERR, "blktmpl error: %s", err);
 			return false;
 		}
-		work->rolltime = blkmk_time_left(work->tmpl, time(NULL));
+		work->rolltime = blkmk_time_left(work->tmpl, tv_now.tv_sec);
 #if BLKMAKER_VERSION > 1
 		if (opt_coinbase_script.sz)
 		{
@@ -2066,7 +2071,7 @@ static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 			}
 		}
 #endif
-		if (blkmk_get_data(work->tmpl, work->data, 80, time(NULL), NULL, &work->dataid) < 76)
+		if (blkmk_get_data(work->tmpl, work->data, 80, tv_now.tv_sec, NULL, &work->dataid) < 76)
 			return false;
 		swap32yes(work->data, work->data, 80 / 4);
 		memcpy(&work->data[80], workpadding_bin, 48);
@@ -2640,7 +2645,7 @@ static void curses_print_status(void)
 
 	wattron(statuswin, A_BOLD);
 	mvwprintw(statuswin, 0, 0, " " PACKAGE " version " VERSION " - Started: %s", datestamp);
-	if (!gettimeofday(&now, NULL))
+	timer_set_now(&now);
 	{
 		unsigned int days, hours;
 		div_t d;
@@ -2851,7 +2856,7 @@ bool log_curses_only(int prio, const char *datetime, const char *str)
 
 	if (curses_active_locked()) {
 		if (!opt_loginput || high_prio) {
-			wprintw(logwin, "%s%s\n", datetime, str);
+			wprintw(logwin, " %s %s\n", datetime, str);
 			if (high_prio) {
 				touchwin(logwin);
 				wrefresh(logwin);
@@ -3156,9 +3161,11 @@ static bool submit_upstream_work_completed(struct work *work, bool resubmit, str
 	int thr_id = work->thr_id;
 	struct pool *pool = work->pool;
 	struct timeval tv_submit_reply;
+	time_t ts_submit_reply;
 	char worktime[200] = "";
 
 	cgtime(&tv_submit_reply);
+	ts_submit_reply = time(NULL);
 
 	if (unlikely(!val)) {
 		applog(LOG_INFO, "submit_upstream_work json_rpc_call failed");
@@ -3191,9 +3198,9 @@ static bool submit_upstream_work_completed(struct work *work, bool resubmit, str
 			double submit_time = tdiff(&tv_submit_reply, ptv_submit);
 			int diffplaces = 3;
 
-			localtime_r(&(work->tv_getwork.tv_sec), tm);
+			localtime_r(&work->ts_getwork, tm);
 			memcpy(&tm_getwork, tm, sizeof(struct tm));
-			localtime_r(&(tv_submit_reply.tv_sec), tm);
+			localtime_r(&ts_submit_reply, tm);
 			memcpy(&tm_submit_reply, tm, sizeof(struct tm));
 
 			if (work->clone) {
@@ -3873,7 +3880,9 @@ static inline bool can_roll(struct work *work)
 static void roll_work(struct work *work)
 {
 	if (work->tmpl) {
-		if (blkmk_get_data(work->tmpl, work->data, 80, time(NULL), NULL, &work->dataid) < 76)
+		struct timeval tv_now;
+		cgtime(&tv_now);
+		if (blkmk_get_data(work->tmpl, work->data, 80, tv_now.tv_sec, NULL, &work->dataid) < 76)
 			applog(LOG_ERR, "Failed to get next data from template; spinning wheels!");
 		swap32yes(work->data, work->data, 80 / 4);
 		calc_midstate(work);
@@ -4089,9 +4098,9 @@ bool stale_work(struct work *work, bool share)
 
 	}
 
-	double elapsed_since_staged = difftime(time(NULL), work->tv_staged.tv_sec);
+	int elapsed_since_staged = timer_elapsed(&work->tv_staged, NULL);
 	if (elapsed_since_staged > work_expiry) {
-		applog(LOG_DEBUG, "%s stale due to expiry (%.0f >= %u)", share?"Share":"Work", elapsed_since_staged, work_expiry);
+		applog(LOG_DEBUG, "%s stale due to expiry (%d >= %u)", share?"Share":"Work", elapsed_since_staged, work_expiry);
 		return true;
 	}
 
@@ -4177,7 +4186,7 @@ struct submit_work_state {
 	bool resubmit;
 	struct curl_ent *ce;
 	int failures;
-	time_t staleexpire;
+	struct timeval tv_staleexpire;
 	char *s;
 	struct timeval tv_submit;
 	struct submit_work_state *next;
@@ -4227,7 +4236,7 @@ static struct submit_work_state *begin_submission(struct work *work)
 			submit_discard_share(work);
 			goto out;
 		}
-		sws->staleexpire = time(NULL) + 300;
+		timer_set_delay_from_now(&sws->tv_staleexpire, 300000000);
 	}
 
 	if (work->stratum) {
@@ -4275,7 +4284,7 @@ static bool retry_submission(struct submit_work_state *sws)
 				submit_discard_share(work);
 				return false;
 			}
-			sws->staleexpire = time(NULL) + 300;
+			timer_set_delay_from_now(&sws->tv_staleexpire, 300000000);
 		}
 		if (unlikely((opt_retries >= 0) && (++sws->failures > opt_retries))) {
 			applog(LOG_ERR, "Pool %d failed %d submission retries, discarding", pool->pool_no, opt_retries);
@@ -4283,7 +4292,8 @@ static bool retry_submission(struct submit_work_state *sws)
 			return false;
 		}
 		else if (work->stale) {
-			if (unlikely(opt_retries < 0 && sws->staleexpire <= time(NULL))) {
+			if (unlikely(opt_retries < 0 && timer_passed(&sws->tv_staleexpire, NULL)))
+			{
 				applog(LOG_NOTICE, "Pool %d stale share failed to submit for 5 minutes, discarding", pool->pool_no);
 				submit_discard_share(work);
 				return false;
@@ -4443,7 +4453,6 @@ next_write_sws_del:
 			char noncehex[9];
 			char ntimehex[9];
 			
-			sshare->sshare_time = time(NULL);
 			sshare->work = copy_work(work);
 			bin2hex(nonce2hex, bytes_buf(&work->nonce2), bytes_len(&work->nonce2));
 			nonce = *((uint32_t *)(work->data + 76));
@@ -4861,7 +4870,7 @@ static void set_curblock(char *hexstr, unsigned char *hash)
 	free(current_fullhash);
 	current_fullhash = malloc(65);
 	bin2hex(current_fullhash, hash_swap, 32);
-	get_timestamp(blocktime, &block_timeval);
+	get_timestamp(blocktime, block_timeval.tv_sec);
 	cg_wunlock(&ch_lock);
 
 	applog(LOG_INFO, "New block: %s diff %s (%s)", current_hash, block_diff, net_hashrate);
@@ -5096,6 +5105,7 @@ static void stage_work(struct work *work)
 	applog(LOG_DEBUG, "Pushing work from pool %d to hash queue", work->pool->pool_no);
 	work->work_restart_id = work->pool->work_restart_id;
 	work->pool->last_work_time = time(NULL);
+	cgtime(&work->pool->tv_last_work_time);
 	test_work_current(work);
 	hash_push(work);
 }
@@ -6393,7 +6403,7 @@ bool parse_stratum_response(struct pool *pool, char *s)
 				applog(LOG_NOTICE, "Pool %u now providing block contents to us",
 				       pool->pool_no);
 			}
-			pool->swork.transparency_time = (time_t)-1;
+			timer_unset(&pool->swork.tv_transparency);
 
 fishy:
 			ret = true;
@@ -6616,7 +6626,7 @@ static bool cnx_needed(struct pool *pool)
 
 	/* Keep the connection open to allow any stray shares to be submitted
 	 * on switching pools for 2 minutes. */
-	if (difftime(time(NULL), pool->last_work_time) < 120)
+	if (!timer_passed(&pool->tv_last_work_time, NULL))
 		return true;
 
 	/* If the pool has only just come to life and is higher priority than
@@ -6787,9 +6797,9 @@ static void *stratum_thread(void *userdata)
 			free_work(work);
 		}
 
-		if (pool->swork.transparency_time != (time_t)-1 && difftime(time(NULL), pool->swork.transparency_time) > 21.09375) {
+		if (timer_passed(&pool->swork.tv_transparency, NULL)) {
 			// More than 4 timmills past since requested transactions
-			pool->swork.transparency_time = (time_t)-1;
+			timer_unset(&pool->swork.tv_transparency);
 			pool->swork.opaque = true;
 			applog(LOG_WARNING, "Pool %u is hiding block contents from us",
 			       pool->pool_no);
@@ -7066,6 +7076,7 @@ retry:
 	pthread_cond_signal(&getq->cond);
 	mutex_unlock(stgd_lock);
 	work->pool->last_work_time = time(NULL);
+	cgtime(&work->pool->tv_last_work_time);
 
 	return work;
 }
@@ -8194,7 +8205,7 @@ static void *watchdog_thread(void __maybe_unused *userdata)
 				continue;
 			else
 			if (*denable == DEV_RECOVER_ERR) {
-				if (opt_restart && difftime(time(NULL), cgpu->device_last_not_well) > cgpu->reinit_backoff) {
+				if (opt_restart && timer_elapsed(&cgpu->tv_device_last_not_well, NULL) > cgpu->reinit_backoff) {
 					applog(LOG_NOTICE, "Attempting to reinitialize %s",
 					       dev_str);
 					if (cgpu->reinit_backoff < 300)
@@ -8210,8 +8221,7 @@ static void *watchdog_thread(void __maybe_unused *userdata)
 					       dev_str);
 					device_recovered(cgpu);
 				}
-				cgpu->device_last_not_well = time(NULL);
-				cgpu->device_not_well_reason = REASON_DEV_THERMAL_CUTOFF;
+				dev_error(cgpu, REASON_DEV_THERMAL_CUTOFF);
 				continue;
 			}
 			else
@@ -9173,6 +9183,7 @@ int main(int argc, char *argv[])
 #endif
 
 	raise_fd_limits();
+	bfg_init_time();
 	
 	if (opt_benchmark) {
 		struct pool *pool;
@@ -9464,11 +9475,12 @@ begin_bench:
 	cgtime(&total_tv_start);
 	cgtime(&total_tv_end);
 	miner_started = total_tv_start;
+	time_t miner_start_ts = time(NULL);
 	if (schedstart.tm.tm_sec)
-		localtime_r(&miner_started.tv_sec, &schedstart.tm);
+		localtime_r(&miner_start_ts, &schedstart.tm);
 	if (schedstop.tm.tm_sec)
-		localtime_r(&miner_started.tv_sec, &schedstop .tm);
-	get_datestamp(datestamp, &total_tv_start);
+		localtime_r(&miner_start_ts, &schedstop .tm);
+	get_datestamp(datestamp, miner_start_ts);
 
 	// Initialise processors and threads
 	k = 0;
@@ -9614,11 +9626,13 @@ retry:
 				{}
 			else
 			if (can_roll(last_work) && should_roll(last_work)) {
+				struct timeval tv_now;
+				cgtime(&tv_now);
 				free_work(work);
 				work = make_clone(pool->last_work_copy);
 				mutex_unlock(&pool->last_work_lock);
 				roll_work(work);
-				applog(LOG_DEBUG, "Generated work from latest GBT job in get_work_thread with %d seconds left", (int)blkmk_time_left(work->tmpl, time(NULL)));
+				applog(LOG_DEBUG, "Generated work from latest GBT job in get_work_thread with %d seconds left", (int)blkmk_time_left(work->tmpl, tv_now.tv_sec));
 				stage_work(work);
 				continue;
 			} else if (last_work->tmpl && pool->proto == PLP_GETBLOCKTEMPLATE && blkmk_work_left(last_work->tmpl) > (unsigned long)mining_threads) {

+ 6 - 2
miner.h

@@ -543,6 +543,7 @@ struct cgpu_info {
 
 	time_t device_last_well;
 	time_t device_last_not_well;
+	struct timeval tv_device_last_not_well;
 	enum dev_reason device_not_well_reason;
 	float reinit_backoff;
 	int thread_fail_init_count;
@@ -1072,7 +1073,7 @@ struct stratum_work {
 	double diff;
 
 	bool transparency_probed;
-	time_t transparency_time;
+	struct timeval tv_transparency;
 	bool opaque;
 };
 
@@ -1144,6 +1145,7 @@ struct pool {
 	struct submit_work_state *sws_waiting_on_curl;
 
 	time_t last_work_time;
+	struct timeval tv_last_work_time;
 	time_t last_share_time;
 	double last_share_diff;
 	uint64_t best_diff;
@@ -1237,6 +1239,7 @@ struct work {
 	bool		do_foreign_submit;
 
 	struct timeval	tv_getwork;
+	time_t		ts_getwork;
 	struct timeval	tv_getwork_reply;
 	struct timeval	tv_cloned;
 	struct timeval	tv_work_start;
@@ -1248,7 +1251,8 @@ struct work {
 	struct work *next;
 };
 
-extern void get_datestamp(char *, struct timeval *);
+extern void get_datestamp(char *, time_t);
+#define get_now_datestamp(buf)  get_datestamp(buf, INVALID_TIMESTAMP)
 extern void inc_hw_errors(struct thr_info *, const struct work *, const uint32_t bad_nonce);
 #define inc_hw_errors_only(thr)  inc_hw_errors(thr, NULL, 0)
 enum test_nonce2_result {

+ 136 - 8
util.c

@@ -1098,12 +1098,11 @@ void nusleep(unsigned int usecs)
 #endif
 }
 
-/* This is a cgminer gettimeofday wrapper. Since we always call gettimeofday
- * with tz set to NULL, and windows' default resolution is only 15ms, this
- * gives us higher resolution times on windows. */
-void cgtime(struct timeval *tv)
+static
+void _now_gettimeofday(struct timeval *tv)
 {
 #ifdef WIN32
+	// Windows' default resolution is only 15ms. This requests 1ms.
 	timeBeginPeriod(1);
 #endif
 	gettimeofday(tv, NULL);
@@ -1112,6 +1111,134 @@ void cgtime(struct timeval *tv)
 #endif
 }
 
+#ifdef HAVE_POOR_GETTIMEOFDAY
+static struct timeval tv_timeofday_offset;
+static struct timeval _tv_timeofday_lastchecked;
+static pthread_mutex_t _tv_timeofday_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static
+void bfg_calibrate_timeofday(struct timeval *expected, char *buf)
+{
+	struct timeval actual, delta;
+	timeradd(expected, &tv_timeofday_offset, expected);
+	_now_gettimeofday(&actual);
+	if (expected->tv_sec >= actual.tv_sec - 1 && expected->tv_sec <= actual.tv_sec + 1)
+		// Within reason - no change necessary
+		return;
+	
+	timersub(&actual, expected, &delta);
+	timeradd(&tv_timeofday_offset, &delta, &tv_timeofday_offset);
+	sprintf(buf, "Recalibrating timeofday offset (delta %ld.%06lds)", (long)delta.tv_sec, (long)delta.tv_usec);
+	*expected = actual;
+}
+
+void bfg_gettimeofday(struct timeval *out)
+{
+	char buf[64] = "";
+	timer_set_now(out);
+	mutex_lock(&_tv_timeofday_mutex);
+	if (_tv_timeofday_lastchecked.tv_sec < out->tv_sec - 21)
+		bfg_calibrate_timeofday(out, buf);
+	else
+		timeradd(out, &tv_timeofday_offset, out);
+	mutex_unlock(&_tv_timeofday_mutex);
+	if (unlikely(buf[0]))
+		applog(LOG_WARNING, "%s", buf);
+}
+#endif
+
+#ifdef WIN32
+static LARGE_INTEGER _perffreq;
+
+static
+void _now_queryperformancecounter(struct timeval *tv)
+{
+	LARGE_INTEGER now;
+	if (unlikely(!QueryPerformanceCounter(&now)))
+		quit(1, "QueryPerformanceCounter failed");
+	
+	*tv = (struct timeval){
+		.tv_sec = now.QuadPart / _perffreq.QuadPart,
+		.tv_usec = (now.QuadPart % _perffreq.QuadPart) * 1000000 / _perffreq.QuadPart,
+	};
+}
+#endif
+
+static
+void _now_is_not_set(__maybe_unused struct timeval *tv)
+{
+	// Might be unclean to swap algorithms after getting a timer
+	quit(1, "timer_set_now called before bfg_init_time");
+}
+
+void (*timer_set_now)(struct timeval *tv) = _now_is_not_set;
+
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC
+static clockid_t bfg_timer_clk;
+
+static
+void _now_clock_gettime(struct timeval *tv)
+{
+	struct timespec ts;
+	if (unlikely(clock_gettime(bfg_timer_clk, &ts)))
+		quit(1, "clock_gettime failed");
+	
+	*tv = (struct timeval){
+		.tv_sec = ts.tv_sec,
+		.tv_usec = ts.tv_nsec / 1000,
+	};
+}
+
+static
+bool _bfg_try_clock_gettime(clockid_t clk)
+{
+	struct timespec ts;
+	if (clock_gettime(clk, &ts))
+		return false;
+	
+	bfg_timer_clk = clk;
+	timer_set_now = _now_clock_gettime;
+	return true;
+}
+#endif
+
+void bfg_init_time()
+{
+	if (timer_set_now != _now_is_not_set)
+		return;
+	
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW
+	if (_bfg_try_clock_gettime(CLOCK_MONOTONIC_RAW))
+		applog(LOG_DEBUG, "Timers: Using clock_gettime(CLOCK_MONOTONIC_RAW)");
+	else
+#endif
+	if (_bfg_try_clock_gettime(CLOCK_MONOTONIC))
+		applog(LOG_DEBUG, "Timers: Using clock_gettime(CLOCK_MONOTONIC)");
+	else
+#endif
+#ifdef WIN32
+	if (QueryPerformanceFrequency(&_perffreq) && _perffreq.QuadPart)
+	{
+		timer_set_now = _now_queryperformancecounter;
+		applog(LOG_DEBUG, "Timers: Using QueryPerformanceCounter");
+	}
+	else
+#endif
+	{
+		timer_set_now = _now_gettimeofday;
+		applog(LOG_DEBUG, "Timers: Using gettimeofday");
+	}
+	
+#ifdef HAVE_POOR_GETTIMEOFDAY
+	char buf[64] = "";
+	struct timeval tv;
+	timer_set_now(&tv);
+	bfg_calibrate_timeofday(&tv, buf);
+	applog(LOG_DEBUG, "%s", buf);
+#endif
+}
+
 void subtime(struct timeval *a, struct timeval *b)
 {
 	timersub(a, b, b);
@@ -1503,8 +1630,8 @@ void stratum_probe_transparency(struct pool *pool)
 	        pool->swork.job_id,
 	        pool->swork.job_id);
 	stratum_send(pool, s, sLen);
-	if ((!pool->swork.opaque) && pool->swork.transparency_time == (time_t)-1)
-		pool->swork.transparency_time = time(NULL);
+	if ((!pool->swork.opaque) && !timer_isset(&pool->swork.tv_transparency))
+		cgtime(&pool->swork.tv_transparency);
 	pool->swork.transparency_probed = true;
 }
 
@@ -1590,7 +1717,7 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	pool->getwork_requested++;
 	total_getworks++;
 
-	if ((merkles && (!pool->swork.transparency_probed || rand() <= RAND_MAX / (opt_skip_checks + 1))) || pool->swork.transparency_time != (time_t)-1)
+	if ((merkles && (!pool->swork.transparency_probed || rand() <= RAND_MAX / (opt_skip_checks + 1))) || timer_isset(&pool->swork.tv_transparency))
 		if (pool->stratum_init)
 			stratum_probe_transparency(pool);
 
@@ -1839,7 +1966,7 @@ static bool setup_stratum_curl(struct pool *pool)
 
 	applog(LOG_DEBUG, "initiate_stratum with sockbuf=%p", pool->sockbuf);
 	mutex_lock(&pool->stratum_lock);
-	pool->swork.transparency_time = (time_t)-1;
+	timer_unset(&pool->swork.tv_transparency);
 	pool->stratum_active = false;
 	pool->stratum_notify = false;
 	pool->swork.transparency_probed = false;
@@ -2099,6 +2226,7 @@ bool restart_stratum(struct pool *pool)
 void dev_error(struct cgpu_info *dev, enum dev_reason reason)
 {
 	dev->device_last_not_well = time(NULL);
+	cgtime(&dev->tv_device_last_not_well);
 	dev->device_not_well_reason = reason;
 
 	switch (reason) {

+ 60 - 7
util.h

@@ -13,11 +13,15 @@
 #ifndef __UTIL_H__
 #define __UTIL_H__
 
+#include <sys/time.h>
+
 #include <curl/curl.h>
 #include <jansson.h>
 
 #include "compat.h"
 
+#define INVALID_TIMESTAMP ((time_t)-1)
+
 #if defined(unix) || defined(__APPLE__)
 	#include <errno.h>
 	#include <sys/socket.h>
@@ -90,7 +94,6 @@ void thr_info_freeze(struct thr_info *thr);
 void thr_info_cancel(struct thr_info *thr);
 void nmsleep(unsigned int msecs);
 void nusleep(unsigned int usecs);
-void cgtime(struct timeval *tv);
 void subtime(struct timeval *a, struct timeval *b);
 void addtime(struct timeval *a, struct timeval *b);
 bool time_more(struct timeval *a, struct timeval *b);
@@ -209,6 +212,22 @@ void set_maxfd(int *p_maxfd, int fd)
 }
 
 
+static inline
+void timer_unset(struct timeval *tvp)
+{
+	tvp->tv_sec = -1;
+}
+
+static inline
+bool timer_isset(const struct timeval *tvp)
+{
+	return tvp->tv_sec != -1;
+}
+
+extern void (*timer_set_now)(struct timeval *);
+extern void bfg_init_time();
+#define cgtime(tvp)  timer_set_now(tvp)
+
 #define TIMEVAL_USECS(usecs)  (  \
 	(struct timeval){  \
 		.tv_sec = (usecs) / 1000000,  \
@@ -223,29 +242,63 @@ void set_maxfd(int *p_maxfd, int fd)
 
 #define timer_set_delay_from_now(tvp_timer, usecs)  do {  \
 	struct timeval tv_now;  \
-	gettimeofday(&tv_now, NULL);  \
+	timer_set_now(&tv_now);  \
 	timer_set_delay(tvp_timer, &tv_now, usecs);  \
 } while(0)
 
 static inline
-bool timer_passed(struct timeval *tvp_timer, struct timeval *tvp_now)
+const struct timeval *_bfg_nullisnow(const struct timeval *tvp, struct timeval *tvp_buf)
+{
+	if (tvp)
+		return tvp;
+	cgtime(tvp_buf);
+	return tvp_buf;
+}
+
+static inline
+int timer_elapsed(const struct timeval *tvp_timer, const struct timeval *tvp_now)
 {
-	return (tvp_timer->tv_sec != -1 && timercmp(tvp_timer, tvp_now, <));
+	struct timeval tv;
+	const struct timeval *_tvp_now = _bfg_nullisnow(tvp_now, &tv);
+	timersub(_tvp_now, tvp_timer, &tv);
+	return tv.tv_sec;
 }
 
+static inline
+bool timer_passed(const struct timeval *tvp_timer, const struct timeval *tvp_now)
+{
+	if (!timer_isset(tvp_timer))
+		return false;
+	
+	struct timeval tv;
+	const struct timeval *_tvp_now = _bfg_nullisnow(tvp_now, &tv);
+	
+	return timercmp(tvp_timer, _tvp_now, <);
+}
+
+#if defined(WIN32) && !defined(HAVE_POOR_GETTIMEOFDAY)
+#define HAVE_POOR_GETTIMEOFDAY
+#endif
+
+#ifdef HAVE_POOR_GETTIMEOFDAY
+extern void bfg_gettimeofday(struct timeval *);
+#else
+#define bfg_gettimeofday(out)  gettimeofday(out, NULL)
+#endif
+
 static inline
 void reduce_timeout_to(struct timeval *tvp_timeout, struct timeval *tvp_time)
 {
-	if (tvp_time->tv_sec == -1)
+	if (!timer_isset(tvp_time))
 		return;
-	if (tvp_timeout->tv_sec == -1 /* no timeout */ || timercmp(tvp_time, tvp_timeout, <))
+	if ((!timer_isset(tvp_timeout)) || timercmp(tvp_time, tvp_timeout, <))
 		*tvp_timeout = *tvp_time;
 }
 
 static inline
 struct timeval *select_timeout(struct timeval *tvp_timeout, struct timeval *tvp_now)
 {
-	if (tvp_timeout->tv_sec == -1)
+	if (!timer_isset(tvp_timeout))
 		return NULL;
 	
 	if (timercmp(tvp_timeout, tvp_now, <))