Browse Source

Merge branch 'cgsleep2' into cg_merges_20130822a

Conflicts:
	Makefile.am
	configure.ac
	driver-avalon.c
	driver-bflsc.c
	driver-bitforce.c
	driver-modminer.c
	driver-ztex.c
	libztex.c
	miner.c
	usbutils.c
	util.c
	util.h
Luke Dashjr 12 years ago
parent
commit
3183f70477
16 changed files with 271 additions and 88 deletions
  1. 1 1
      Makefile.am
  2. 1 1
      adl.c
  3. 2 2
      api.c
  4. 3 0
      configure.ac
  5. 2 2
      deviceapi.c
  6. 3 3
      driver-avalon.c
  7. 4 4
      driver-bitforce.c
  8. 1 1
      driver-cairnsmore.c
  9. 1 1
      driver-icarus.c
  10. 1 1
      driver-modminer.c
  11. 1 1
      driver-x6500.c
  12. 2 2
      driver-ztex.c
  13. 2 2
      libztex.c
  14. 12 6
      miner.c
  15. 219 59
      util.c
  16. 16 2
      util.h

+ 1 - 1
Makefile.am

@@ -43,7 +43,7 @@ bin_PROGRAMS	= bfgminer
 bfgminer_LDFLAGS	= $(PTHREAD_FLAGS)
 bfgminer_LDADD	= $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ \
 		  @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \
-		  @UDEV_LIBS@ @LIBUSB_LIBS@ @MM_LIBS@ \
+		  @UDEV_LIBS@ @LIBUSB_LIBS@ @MM_LIBS@ @RT_LIBS@ \
 		  @MATH_LIBS@ lib/libgnu.a ccan/libccan.a
 bfgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @LIBUSB_CFLAGS@ @LIBCURL_CFLAGS@
 

+ 1 - 1
adl.c

@@ -1380,7 +1380,7 @@ updated:
 		clear_logwin();
 		return;
 	}
-	nmsleep(1000);
+	cgsleep_ms(1000);
 	goto updated;
 }
 #endif

+ 2 - 2
api.c

@@ -3707,7 +3707,7 @@ void api(int api_thr_id)
 
 	/* This should be done before curl in needed
 	 * to ensure curl has already called WSAStartup() in windows */
-	nmsleep(opt_log_interval*1000);
+	cgsleep_ms(opt_log_interval*1000);
 
 	*apisock = socket(AF_INET, SOCK_STREAM, 0);
 	if (*apisock == INVSOCK) {
@@ -3753,7 +3753,7 @@ void api(int api_thr_id)
 				break;
 			else {
 				applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port);
-				nmsleep(30000);
+				cgsleep_ms(30000);
 			}
 		} else
 			bound = 1;

+ 3 - 0
configure.ac

@@ -83,6 +83,7 @@ DLOPEN_FLAGS="-ldl"
 WS2_LIBS=""
 MM_LIBS=""
 MATH_LIBS="-lm"
+RT_LIBS="-lrt"
 
 case $target in
   amd64-* | x86_64-*)
@@ -107,6 +108,7 @@ case $target in
     DLOPEN_FLAGS=""
     WS2_LIBS="-lws2_32"
     MM_LIBS="-lwinmm"
+    RT_LIBS=""
     AC_DEFINE([_WIN32_WINNT], [0x0501], "WinNT version for XP+ support")
     AC_DEFINE([FD_SETSIZE], [4096], [Maximum sockets before fd_set overflows])
     ;;
@@ -953,6 +955,7 @@ AC_SUBST(PDCURSES_LIBS)
 AC_SUBST(WS2_LIBS)
 AC_SUBST(MM_LIBS)
 AC_SUBST(MATH_LIBS)
+AC_SUBST(RT_LIBS)
 AC_SUBST(UDEV_LIBS)
 AC_SUBST(SSE2_CFLAGS)
 AC_SUBST(YASM_FMT)

+ 2 - 2
deviceapi.c

@@ -99,7 +99,7 @@ int restart_wait(struct thr_info *thr, unsigned int mstime)
 	{
 		// This is a bug!
 		applog(LOG_ERR, "%"PRIpreprv": restart_wait called without a work_restart_notifier", thr->cgpu->proc_repr);
-		nmsleep(mstime);
+		cgsleep_ms(mstime);
 		return (thr->work_restart ? 0 : ETIMEDOUT);
 	}
 	
@@ -595,7 +595,7 @@ out: ;
 	while ( (proc = proc->next_proc) && !proc->threads);
 	mythr->getwork = 0;
 	mythr->has_pth = false;
-	nmsleep(1000);
+	cgsleep_ms(1000);
 	
 	if (drv->thread_shutdown)
 		drv->thread_shutdown(mythr);

+ 3 - 3
driver-avalon.c

@@ -706,7 +706,7 @@ static void do_avalon_close(struct thr_info *thr)
 	struct avalon_info *info = avalon->device_data;
 
 	avalon_free_work(thr);
-	nmsleep(1000);
+	cgsleep_ms(1000);
 	avalon_reset(avalon->device_fd, &ar);
 	avalon_idle(avalon);
 	avalon_close(avalon->device_fd);
@@ -853,7 +853,7 @@ static int64_t avalon_scanhash(struct thr_info *thr)
 			       avalon->device_id);
 			dev_error(avalon, REASON_DEV_COMMS_ERROR);
 			first_try = 0;
-			nmsleep(1000);
+			cgsleep_ms(1000);
 			avalon_init(avalon);
 			return 0;	/* This should never happen */
 		}
@@ -939,7 +939,7 @@ static int64_t avalon_scanhash(struct thr_info *thr)
 			"AVA%i: FPGA controller messed up, %d wrong results",
 			avalon->device_id, result_wrong);
 		dev_error(avalon, REASON_DEV_COMMS_ERROR);
-		nmsleep(1000);
+		cgsleep_ms(1000);
 		avalon_init(avalon);
 		return 0;
 	}

+ 4 - 4
driver-bitforce.c

@@ -426,7 +426,7 @@ void bitforce_reinit(struct cgpu_info *bitforce)
 
 	if (fdDev) {
 		BFclose(fdDev);
-		nmsleep(5000);
+		cgsleep_ms(5000);
 		*p_fdDev = 0;
 	}
 
@@ -449,7 +449,7 @@ void bitforce_reinit(struct cgpu_info *bitforce)
 		}
 
 		if (retries++)
-			nmsleep(10);
+			cgsleep_ms(10);
 	} while (strstr(pdevbuf, "BUSY") && (retries * 10 < BITFORCE_TIMEOUT_MS));
 
 	if (unlikely(!strstr(pdevbuf, "SHA256"))) {
@@ -517,7 +517,7 @@ static void bitforce_flash_led(struct cgpu_info *bitforce)
 
 	/* However, this stops anything else getting a reply
 	 * So best to delay any other access to the BFL */
-	nmsleep(4000);
+	cgsleep_ms(4000);
 
 	mutex_unlock(mutexp);
 
@@ -1365,7 +1365,7 @@ static bool bitforce_thread_init(struct thr_info *thr)
 	 * so the devices aren't making calls all at the same time. */
 	wait = thr->id * MAX_START_DELAY_MS;
 	applog(LOG_DEBUG, "%s: Delaying start by %dms", bitforce->dev_repr, wait / 1000);
-	nmsleep(wait);
+	cgsleep_ms(wait);
 
 	if (sc)
 	{

+ 1 - 1
driver-cairnsmore.c

@@ -190,7 +190,7 @@ static bool cairnsmore_identify(struct cgpu_info *cm1)
 		return false;
 	
 	cairnsmore_send_cmd(cm1->device_fd, 1, 1);
-	nmsleep(5000);
+	cgsleep_ms(5000);
 	cairnsmore_send_cmd(cm1->device_fd, 1, 0);
 	cm1->flash_led = true;
 	return true;

+ 1 - 1
driver-icarus.c

@@ -844,7 +844,7 @@ void handle_identify(struct thr_info * const thr, int ret, const bool was_first_
 	
 	// 3. Delay 3 more seconds
 	applog(LOG_DEBUG, "%"PRIpreprv": Identify: Leaving idle for 3 seconds", icarus->proc_repr);
-	nmsleep(3000);
+	cgsleep_ms(3000);
 	
 	// Check for work restart in the meantime
 	if (thr->work_restart)

+ 1 - 1
driver-modminer.c

@@ -664,7 +664,7 @@ modminer_process_results(struct thr_info*thr)
 		}
 		if (work_restart(thr) || !--iter)
 			break;
-		nmsleep(1);
+		cgsleep_ms(1);
 		if (work_restart(thr))
 			break;
 		mutex_lock(mutexp);

+ 1 - 1
driver-x6500.c

@@ -255,7 +255,7 @@ x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
 	x6500_jtag_set(jp, 0x11);
 	jtag_write(jp, JTAG_REG_IR, "\xa0", 6);  // CFG_IN
 	
-	nmsleep(1000);
+	cgsleep_ms(1000);
 	
 	if (fread(buf, 32, 1, f) != 1)
 		bailout2(LOG_ERR, "%s: File underrun programming %s (%lu bytes left)", x6500->dev_repr, x6500->device_path, len);

+ 2 - 2
driver-ztex.c

@@ -190,7 +190,7 @@ static int64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 	if (i < 0) {
 		// Something wrong happened in send
 		applog(LOG_ERR, "%"PRIpreprv": Failed to send hash data with err %d, retrying", cgpu->proc_repr, i);
-		nmsleep(500);
+		cgsleep_ms(500);
 		i = libztex_sendHashData(ztex, sendbuf);
 		if (i < 0) {
 			// And there's nothing we can do about it
@@ -235,7 +235,7 @@ static int64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 		if (i < 0) {
 			// Something wrong happened in read
 			applog(LOG_ERR, "%"PRIpreprv": Failed to read hash data with err %d, retrying", cgpu->proc_repr, i);
-			nmsleep(500);
+			cgsleep_ms(500);
 			i = libztex_readHashData(ztex, &hdata[0]);
 			if (i < 0) {
 				// And there's nothing we can do about it

+ 2 - 2
libztex.c

@@ -404,7 +404,7 @@ static int libztex_configureFpgaHS(struct libztex_device *ztex, const char* firm
 
 	libusb_release_interface(ztex->hndl, settings[1]);
 
-	nmsleep(200);
+	cgsleep_ms(200);
 	applog(LOG_INFO, "%"PRIpreprv": HS FPGA configuration done", repr);
 	return 0;
 }
@@ -469,7 +469,7 @@ static int libztex_configureFpgaLS(struct libztex_device *ztex, const char* firm
 		return -3;
 	}
 
-	nmsleep(200);
+	cgsleep_ms(200);
 	applog(LOG_INFO, "%"PRIpreprv": FPGA configuration done", repr);
 	return 0;
 }

+ 12 - 6
miner.c

@@ -45,6 +45,7 @@
 #include <sys/socket.h>
 #else
 #include <winsock2.h>
+#include <windows.h>
 #endif
 #include <ccan/opt/opt.h>
 #include <jansson.h>
@@ -7281,7 +7282,7 @@ static void *stratum_thread(void *userdata)
 				while (!restart_stratum(pool)) {
 					if (pool->removed)
 						goto out;
-					nmsleep(30000);
+					cgsleep_ms(30000);
 				}
 			}
 		}
@@ -8419,7 +8420,7 @@ retry_pool:
 	if (!pool) {
 		applog(LOG_WARNING, "No suitable long-poll found for %s", cp->rpc_url);
 		while (!pool) {
-			nmsleep(60000);
+			cgsleep_ms(60000);
 			pool = select_longpoll_pool(cp);
 		}
 	}
@@ -8496,7 +8497,7 @@ retry_pool:
 			if (failures == 1)
 				applog(LOG_WARNING, "longpoll failed for %s, retrying every 30s", lp_url);
 lpfail:
-			nmsleep(30000);
+			cgsleep_ms(30000);
 		}
 
 		if (pool != cp) {
@@ -8647,7 +8648,7 @@ static void *watchpool_thread(void __maybe_unused *userdata)
 			switch_pools(NULL);
 		}
 
-		nmsleep(30000);
+		cgsleep_ms(30000);
 			
 	}
 	return NULL;
@@ -9021,6 +9022,9 @@ void _bfg_clean_up(void)
 #endif
 
 	cgtime(&total_tv_end);
+#ifdef WIN32
+	timeEndPeriod(1);
+#endif
 #ifdef HAVE_CURSES
 	disable_curses();
 #endif
@@ -9767,6 +9771,8 @@ int main(int argc, char *argv[])
 	sigaction(SIGINT, &handler, &inthandler);
 #ifndef WIN32
 	signal(SIGPIPE, SIG_IGN);
+#else
+	timeBeginPeriod(1);
 #endif
 	opt_kernel_path = alloca(PATH_MAX);
 	strcpy(opt_kernel_path, CGMINER_PREFIX);
@@ -10266,7 +10272,7 @@ retry:
 				struct pool *altpool = select_pool(true);
 
 				if (altpool == pool && pool->has_stratum)
-					nmsleep(5000);
+					cgsleep_ms(5000);
 				pool = altpool;
 				goto retry;
 			}
@@ -10328,7 +10334,7 @@ retry:
 			next_pool = select_pool(!opt_fail_only);
 			if (pool == next_pool) {
 				applog(LOG_DEBUG, "Pool %d json_rpc_call failed on get work, retrying in 5s", pool->pool_no);
-				nmsleep(5000);
+				cgsleep_ms(5000);
 			} else {
 				applog(LOG_DEBUG, "Pool %d json_rpc_call failed on get work, failover activated", pool->pool_no);
 				pool = next_pool;

+ 219 - 59
util.c

@@ -1053,65 +1053,7 @@ void setup_pthread_cancel_workaround()
 
 #endif
 
-/* Provide a ms based sleep that uses nanosleep to avoid poor usleep accuracy
- * on SMP machines */
-void nmsleep(unsigned int msecs)
-{
-	struct timespec twait, tleft;
-	int ret;
-	ldiv_t d;
-
-#ifdef WIN32
-	timeBeginPeriod(1);
-#endif
-	d = ldiv(msecs, 1000);
-	tleft.tv_sec = d.quot;
-	tleft.tv_nsec = d.rem * 1000000;
-	do {
-		twait.tv_sec = tleft.tv_sec;
-		twait.tv_nsec = tleft.tv_nsec;
-		ret = nanosleep(&twait, &tleft);
-	} while (ret == -1 && errno == EINTR);
-#ifdef WIN32
-	timeEndPeriod(1);
-#endif
-}
-
-/* Same for usecs */
-void nusleep(unsigned int usecs)
-{
-	struct timespec twait, tleft;
-	int ret;
-	ldiv_t d;
-
-#ifdef WIN32
-	timeBeginPeriod(1);
-#endif
-	d = ldiv(usecs, 1000000);
-	tleft.tv_sec = d.quot;
-	tleft.tv_nsec = d.rem * 1000;
-	do {
-		twait.tv_sec = tleft.tv_sec;
-		twait.tv_nsec = tleft.tv_nsec;
-		ret = nanosleep(&twait, &tleft);
-	} while (ret == -1 && errno == EINTR);
-#ifdef WIN32
-	timeEndPeriod(1);
-#endif
-}
-
-static
-void _now_gettimeofday(struct timeval *tv)
-{
-#ifdef WIN32
-	// Windows' default resolution is only 15ms. This requests 1ms.
-	timeBeginPeriod(1);
-#endif
-	gettimeofday(tv, NULL);
-#ifdef WIN32
-	timeEndPeriod(1);
-#endif
-}
+static void _now_gettimeofday(struct timeval *);
 
 #ifdef HAVE_POOR_GETTIMEOFDAY
 static struct timeval tv_timeofday_offset;
@@ -1266,6 +1208,224 @@ void copy_time(struct timeval *dest, const struct timeval *src)
 	memcpy(dest, src, sizeof(struct timeval));
 }
 
+void timespec_to_val(struct timeval *val, const struct timespec *spec)
+{
+	val->tv_sec = spec->tv_sec;
+	val->tv_usec = spec->tv_nsec / 1000;
+}
+
+void timeval_to_spec(struct timespec *spec, const struct timeval *val)
+{
+	spec->tv_sec = val->tv_sec;
+	spec->tv_nsec = val->tv_usec * 1000;
+}
+
+void us_to_timeval(struct timeval *val, int64_t us)
+{
+	lldiv_t tvdiv = lldiv(us, 1000000);
+
+	val->tv_sec = tvdiv.quot;
+	val->tv_usec = tvdiv.rem;
+}
+
+void us_to_timespec(struct timespec *spec, int64_t us)
+{
+	lldiv_t tvdiv = lldiv(us, 1000000);
+
+	spec->tv_sec = tvdiv.quot;
+	spec->tv_nsec = tvdiv.rem * 1000;
+}
+
+void ms_to_timespec(struct timespec *spec, int64_t ms)
+{
+	lldiv_t tvdiv = lldiv(ms, 1000);
+
+	spec->tv_sec = tvdiv.quot;
+	spec->tv_nsec = tvdiv.rem * 1000000;
+}
+
+void timeraddspec(struct timespec *a, const struct timespec *b)
+{
+	a->tv_sec += b->tv_sec;
+	a->tv_nsec += b->tv_nsec;
+	if (a->tv_nsec >= 1000000000) {
+		a->tv_nsec -= 1000000000;
+		a->tv_sec++;
+	}
+}
+
+static int timespec_to_ms(struct timespec *ts)
+{
+	return ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
+}
+
+/* These are cgminer specific sleep functions that use an absolute nanosecond
+ * resolution timer to avoid poor usleep accuracy and overruns. */
+#ifndef WIN32
+void cgtimer_time(cgtimer_t *ts_start)
+{
+	clock_gettime(CLOCK_MONOTONIC, ts_start);
+}
+
+static void nanosleep_abstime(struct timespec *ts_end)
+{
+	int ret;
+
+	do {
+		ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, ts_end, NULL);
+	} while (ret == EINTR);
+}
+
+/* Reentrant version of cgsleep functions allow start time to be set separately
+ * from the beginning of the actual sleep, allowing scheduling delays to be
+ * counted in the sleep. */
+void cgsleep_ms_r(cgtimer_t *ts_start, int ms)
+{
+	struct timespec ts_end;
+
+	ms_to_timespec(&ts_end, ms);
+	timeraddspec(&ts_end, ts_start);
+	nanosleep_abstime(&ts_end);
+}
+
+void cgsleep_us_r(cgtimer_t *ts_start, int64_t us)
+{
+	struct timespec ts_end;
+
+	us_to_timespec(&ts_end, us);
+	timeraddspec(&ts_end, ts_start);
+	nanosleep_abstime(&ts_end);
+}
+
+int cgtimer_to_ms(cgtimer_t *cgt)
+{
+	return timespec_to_ms(cgt);
+}
+
+/* Subtracts b from a and stores it in res. */
+void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res)
+{
+	res->tv_sec = a->tv_sec - b->tv_sec;
+	res->tv_nsec = a->tv_nsec - b->tv_nsec;
+	if (res->tv_nsec < 0) {
+		res->tv_nsec += 1000000000;
+		res->tv_sec--;
+	}
+}
+
+static
+void _now_gettimeofday(struct timeval *tv)
+{
+	gettimeofday(tv, NULL);
+}
+#else
+/* Windows start time is since 1601 lol so convert it to unix epoch 1970. */
+#define EPOCHFILETIME (116444736000000000LL)
+
+/* Return the system time as an lldiv_t in decimicroseconds. */
+static void decius_time(lldiv_t *lidiv)
+{
+	FILETIME ft;
+	LARGE_INTEGER li;
+
+	GetSystemTimeAsFileTime(&ft);
+	li.LowPart  = ft.dwLowDateTime;
+	li.HighPart = ft.dwHighDateTime;
+	li.QuadPart -= EPOCHFILETIME;
+
+	/* SystemTime is in decimicroseconds so divide by an unusual number */
+	*lidiv = lldiv(li.QuadPart, 10000000);
+}
+
+void _now_gettimeofday(struct timeval *tv)
+{
+	lldiv_t lidiv;
+
+	decius_time(&lidiv);
+	tv->tv_sec = lidiv.quot;
+	tv->tv_usec = lidiv.rem / 10;
+}
+
+void cgtimer_time(cgtimer_t *ts_start)
+{
+	lldiv_t lidiv;;
+
+	decius_time(&lidiv);
+	ts_start->tv_sec = lidiv.quot;
+	ts_start->tv_nsec = lidiv.quot * 100;
+}
+
+/* Subtract b from a */
+static void timersubspec(struct timespec *a, const struct timespec *b)
+{
+	a->tv_sec -= b->tv_sec;
+	a->tv_nsec -= b->tv_nsec;
+	if (a->tv_nsec < 0) {
+		a->tv_nsec += 1000000000;
+		a->tv_sec--;
+	}
+}
+
+static void cgsleep_spec(struct timespec *ts_diff, const struct timespec *ts_start)
+{
+	struct timespec now;
+
+	timeraddspec(ts_diff, ts_start);
+	cgtimer_time(&now);
+	timersubspec(ts_diff, &now);
+	if (unlikely(ts_diff->tv_sec < 0))
+		return;
+	nanosleep(ts_diff, NULL);
+}
+
+void cgsleep_ms_r(cgtimer_t *ts_start, int ms)
+{
+	struct timespec ts_diff;
+
+	ms_to_timespec(&ts_diff, ms);
+	cgsleep_spec(&ts_diff, ts_start);
+}
+
+void cgsleep_us_r(cgtimer_t *ts_start, int64_t us)
+{
+	struct timespec ts_diff;
+
+	us_to_timespec(&ts_diff, us);
+	cgsleep_spec(&ts_diff, ts_start);
+}
+
+int cgtimer_to_ms(cgtimer_t *cgt)
+{
+	return timespec_to_ms(cgt);
+}
+
+void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res)
+{
+	res->tv_sec = a->tv_sec - b->tv_sec;
+	res->tv_nsec = a->tv_nsec - b->tv_nsec;
+	if (res->tv_nsec < 0) {
+		res->tv_nsec += 1000000000;;
+		res->tv_sec--;
+	}
+}
+#endif
+
+void cgsleep_ms(int ms)
+{
+	cgtimer_t ts_start;
+
+	cgsleep_prepare_r(&ts_start);
+	cgsleep_ms_r(&ts_start, ms);
+}
+
+void cgsleep_us(int64_t us)
+{
+	cgtimer_t ts_start;
+
+	cgsleep_prepare_r(&ts_start);
+	cgsleep_us_r(&ts_start, us);
+}
+
 /* Returns the microseconds difference between end and start times as a double */
 double us_tdiff(struct timeval *end, struct timeval *start)
 {

+ 16 - 2
util.h

@@ -98,6 +98,8 @@ bool isCspace(int c)
 	}
 }
 
+typedef struct timespec cgtimer_t;
+
 struct thr_info;
 struct pool;
 enum dev_reason;
@@ -117,13 +119,25 @@ extern bool hash_target_check_v(const unsigned char *hash, const unsigned char *
 int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*start) (void *), void *arg);
 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 subtime(struct timeval *a, struct timeval *b);
 void addtime(struct timeval *a, struct timeval *b);
 bool time_more(struct timeval *a, struct timeval *b);
 bool time_less(struct timeval *a, struct timeval *b);
 void copy_time(struct timeval *dest, const struct timeval *src);
+void timespec_to_val(struct timeval *val, const struct timespec *spec);
+void timeval_to_spec(struct timespec *spec, const struct timeval *val);
+void us_to_timeval(struct timeval *val, int64_t us);
+void us_to_timespec(struct timespec *spec, int64_t us);
+void ms_to_timespec(struct timespec *spec, int64_t ms);
+void timeraddspec(struct timespec *a, const struct timespec *b);
+void cgsleep_ms(int ms);
+void cgsleep_us(int64_t us);
+void cgtimer_time(cgtimer_t *ts_start);
+#define cgsleep_prepare_r(ts_start) cgtimer_time(ts_start)
+void cgsleep_ms_r(cgtimer_t *ts_start, int ms);
+void cgsleep_us_r(cgtimer_t *ts_start, int64_t us);
+int cgtimer_to_ms(cgtimer_t *cgt);
+void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res);
 double us_tdiff(struct timeval *end, struct timeval *start);
 double tdiff(struct timeval *end, struct timeval *start);
 bool _stratum_send(struct pool *pool, char *s, ssize_t len, bool force);