diff --git a/NEWS b/NEWS index d4308524..f4d8f5bd 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,28 @@ +Version 3.6.4 - 18th October 2013 + +- Fixing the memory leak for remaining semaphores means we can go back to using +async transfers on other OSes with our own timeout management again. +- Use the forcelog function on shutdown to cope with indeterminate console lock +states due to killing of threads. +- Add a forcelog variant of applog which invalidates any console lock to force +output. +- Send pthread_cancel to failed completion_timeout that has timed out. +- Simplify queued hashtable by storing unqueued work separately in a single +pointer. +- bflsc use getinfo chip parallelization if it is present +- bflsc - fix brackets so [Chips] isn't always null +- Remove unused variables. +- Use cgcompletion timeouts for the unreliable shutdown functions on kill_work. +- Fix cgcompletion return code and free on successful completion. +- Provide a cg_completion_timeout helper function for unreliable functions that +takes arbitrary functions and parameters and reliably returns. +- Perform sync transfers on shutdown to allow final transfers to complete. +- Destroy cgsems used after transfers to not leave open files on osx. +- klondike rewrite work control +- allow __work_complete() access +- miner.h allow devices to tv_stamp work + + Version 3.6.3 - 17th October 2013 - API add 'MHS %ds' to 'summary' diff --git a/cgminer.c b/cgminer.c index 66794481..a4007f63 100644 --- a/cgminer.c +++ b/cgminer.c @@ -427,7 +427,7 @@ static void applog_and_exit(const char *fmt, ...) va_start(ap, fmt); vsnprintf(exit_buf, sizeof(exit_buf), fmt, ap); va_end(ap); - _applog(LOG_ERR, exit_buf); + _applog(LOG_ERR, exit_buf, true); exit(1); } @@ -3166,7 +3166,7 @@ static void kill_mining(void) struct thr_info *thr; int i; - applog(LOG_DEBUG, "Killing off mining threads"); + forcelog(LOG_DEBUG, "Killing off mining threads"); /* Kill the mining threads*/ for (i = 0; i < mining_threads; i++) { pthread_t *pth = NULL; @@ -3193,29 +3193,29 @@ static void __kill_work(void) if (!successful_connect) return; - applog(LOG_INFO, "Received kill message"); + forcelog(LOG_INFO, "Received kill message"); #ifdef USE_USBUTILS /* Best to get rid of it first so it doesn't * try to create any new devices */ if (!opt_scrypt) { - applog(LOG_DEBUG, "Killing off HotPlug thread"); + forcelog(LOG_DEBUG, "Killing off HotPlug thread"); thr = &control_thr[hotplug_thr_id]; kill_timeout(thr); } #endif - applog(LOG_DEBUG, "Killing off watchpool thread"); + forcelog(LOG_DEBUG, "Killing off watchpool thread"); /* Kill the watchpool thread */ thr = &control_thr[watchpool_thr_id]; kill_timeout(thr); - applog(LOG_DEBUG, "Killing off watchdog thread"); + forcelog(LOG_DEBUG, "Killing off watchdog thread"); /* Kill the watchdog thread */ thr = &control_thr[watchdog_thr_id]; kill_timeout(thr); - applog(LOG_DEBUG, "Shutting down mining threads"); + forcelog(LOG_DEBUG, "Shutting down mining threads"); for (i = 0; i < mining_threads; i++) { struct cgpu_info *cgpu; @@ -3233,12 +3233,12 @@ static void __kill_work(void) cg_completion_timeout(&kill_mining, NULL, 3000); - applog(LOG_DEBUG, "Killing off stage thread"); + forcelog(LOG_DEBUG, "Killing off stage thread"); /* Stop the others */ thr = &control_thr[stage_thr_id]; kill_timeout(thr); - applog(LOG_DEBUG, "Killing off API thread"); + forcelog(LOG_DEBUG, "Killing off API thread"); thr = &control_thr[api_thr_id]; kill_timeout(thr); @@ -3246,10 +3246,10 @@ static void __kill_work(void) /* Release USB resources in case it's a restart * and not a QUIT */ if (!opt_scrypt) { - applog(LOG_DEBUG, "Releasing all USB devices"); + forcelog(LOG_DEBUG, "Releasing all USB devices"); cg_completion_timeout(&usb_cleanup, NULL, 1000); - applog(LOG_DEBUG, "Killing off usbres thread"); + forcelog(LOG_DEBUG, "Killing off usbres thread"); thr = &control_thr[usbres_thr_id]; kill_timeout(thr); } diff --git a/configure.ac b/configure.ac index 39b74ccb..27c68baf 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_maj], [3]) m4_define([v_min], [6]) -m4_define([v_mic], [3]) +m4_define([v_mic], [4]) ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_ver], [v_maj.v_min.v_mic]) m4_define([lt_rev], m4_eval(v_maj + v_min)) diff --git a/logging.c b/logging.c index d00600f8..52eb9ef7 100644 --- a/logging.c +++ b/logging.c @@ -21,11 +21,17 @@ bool opt_log_output = false; /* per default priorities higher than LOG_NOTICE are logged */ int opt_log_level = LOG_NOTICE; -static void my_log_curses(int prio, const char *datetime, const char *str) +static void my_log_curses(int prio, const char *datetime, const char *str, bool force) { if (opt_quiet && prio != LOG_ERR) return; + /* Mutex could be locked by dead thread on shutdown so forcelog will + * invalidate any console lock status. */ + if (force) { + mutex_trylock(&console_lock); + mutex_unlock(&console_lock); + } #ifdef HAVE_CURSES extern bool use_curses; if (use_curses && log_curses_only(prio, datetime, str)) @@ -44,7 +50,7 @@ static void my_log_curses(int prio, const char *datetime, const char *str) /* * log function */ -void _applog(int prio, const char *str) +void _applog(int prio, const char *str, bool force) { #ifdef HAVE_SYSLOG_H if (use_syslog) { @@ -77,6 +83,6 @@ void _applog(int prio, const char *str) fflush(stderr); } - my_log_curses(prio, datetime, str); + my_log_curses(prio, datetime, str, force); } } diff --git a/logging.h b/logging.h index 2956452f..4d8a2501 100644 --- a/logging.h +++ b/logging.h @@ -28,7 +28,7 @@ extern int opt_log_level; #define LOGBUFSIZ 256 -extern void _applog(int prio, const char *str); +extern void _applog(int prio, const char *str, bool force); #define IN_FMT_FFL " in %s %s():%d" @@ -37,7 +37,7 @@ extern void _applog(int prio, const char *str); if (use_syslog || opt_log_output || prio <= opt_log_level) { \ char tmp42[LOGBUFSIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ - _applog(prio, tmp42); \ + _applog(prio, tmp42, false); \ } \ } \ } while (0) @@ -47,7 +47,17 @@ extern void _applog(int prio, const char *str); if (use_syslog || opt_log_output || prio <= opt_log_level) { \ char tmp42[_SIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ - _applog(prio, tmp42); \ + _applog(prio, tmp42, false); \ + } \ + } \ +} while (0) + +#define forcelog(prio, fmt, ...) do { \ + if (opt_debug || prio != LOG_DEBUG) { \ + if (use_syslog || opt_log_output || prio <= opt_log_level) { \ + char tmp42[LOGBUFSIZ]; \ + snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ + _applog(prio, tmp42, true); \ } \ } \ } while (0) @@ -56,7 +66,7 @@ extern void _applog(int prio, const char *str); if (fmt) { \ char tmp42[LOGBUFSIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \ - _applog(LOG_ERR, tmp42); \ + _applog(LOG_ERR, tmp42, true); \ } \ _quit(status); \ } while (0) @@ -66,7 +76,7 @@ extern void _applog(int prio, const char *str); char tmp42[LOGBUFSIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt IN_FMT_FFL, \ ##__VA_ARGS__, __FILE__, __func__, __LINE__); \ - _applog(LOG_ERR, tmp42); \ + _applog(LOG_ERR, tmp42, true); \ } \ _quit(status); \ } while (0) @@ -76,7 +86,7 @@ extern void _applog(int prio, const char *str); char tmp42[LOGBUFSIZ]; \ snprintf(tmp42, sizeof(tmp42), fmt IN_FMT_FFL, \ ##__VA_ARGS__, _file, _func, _line); \ - _applog(LOG_ERR, tmp42); \ + _applog(LOG_ERR, tmp42, true); \ } \ _quit(status); \ } while (0) diff --git a/usbutils.c b/usbutils.c index d66d722e..6ad2245d 100644 --- a/usbutils.c +++ b/usbutils.c @@ -968,7 +968,7 @@ void usb_all(int level) for (i = 0; i < count; i++) usb_full(&j, list[i], &buf, &off, &len, level); - _applog(LOG_WARNING, buf); + _applog(LOG_WARNING, buf, false); free(buf); @@ -2267,6 +2267,33 @@ static void LIBUSB_CALL transfer_callback(struct libusb_transfer *transfer) cgsem_post(&ut->cgsem); } +static int usb_transfer_toerr(int ret) +{ + switch (ret) { + default: + case LIBUSB_TRANSFER_COMPLETED: + ret = LIBUSB_SUCCESS; + break; + case LIBUSB_TRANSFER_ERROR: + ret = LIBUSB_ERROR_IO; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + case LIBUSB_TRANSFER_CANCELLED: + ret = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + ret = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + ret = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + ret = LIBUSB_ERROR_OVERFLOW; + break; + } + return ret; +} + /* Wait for callback function to tell us it has finished the USB transfer, but * use our own timer to cancel the request if we go beyond the timeout. */ static int callback_wait(struct usb_transfer *ut, int *transferred, unsigned int timeout) @@ -2283,8 +2310,7 @@ static int callback_wait(struct usb_transfer *ut, int *transferred, unsigned int cgsem_wait(&ut->cgsem); } ret = transfer->status; - if (ret == LIBUSB_TRANSFER_CANCELLED || ret == LIBUSB_TRANSFER_TIMED_OUT) - ret = LIBUSB_ERROR_TIMEOUT; + ret = usb_transfer_toerr(ret); /* No need to sort out mutexes here since they won't be reused */ *transferred = transfer->actual_length; @@ -2331,15 +2357,9 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo, USBDEBUG("USB debug: @usb_bulk_transfer(%s (nodev=%s),intinfo=%d,epinfo=%d,data=%p,length=%d,timeout=%u,mode=%d,cmd=%s,seq=%d) endpoint=%d", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), intinfo, epinfo, data, length, timeout, mode, usb_cmdname(cmd), seq, (int)endpoint); init_usb_transfer(&ut); -#ifdef LINUX /* We give the transfer no timeout since we manage timeouts ourself */ libusb_fill_bulk_transfer(ut.transfer, dev_handle, endpoint, buf, length, transfer_callback, &ut, 0); -#else - /* All other OSes not so lucky */ - libusb_fill_bulk_transfer(ut.transfer, dev_handle, endpoint, buf, length, - transfer_callback, &ut, timeout); -#endif STATS_TIMEVAL(&tv_start); cg_rlock(&cgusb_fd_lock); err = libusb_submit_transfer(ut.transfer); @@ -2357,7 +2377,7 @@ usb_bulk_transfer(struct libusb_device_handle *dev_handle, int intinfo, cgpu->drv->name, cgpu->device_id, usb_cmdname(cmd), *transferred, err, errn); - if (err == LIBUSB_ERROR_PIPE || err == LIBUSB_TRANSFER_STALL) { + if (err == LIBUSB_ERROR_PIPE) { int retries = 0; do { @@ -2775,25 +2795,19 @@ static int usb_control_transfer(struct cgpu_info *cgpu, libusb_device_handle *de wIndex, wLength); if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) memcpy(buf + LIBUSB_CONTROL_SETUP_SIZE, buffer, wLength); -#ifdef LINUX libusb_fill_control_transfer(ut.transfer, dev_handle, buf, transfer_callback, &ut, 0); -#else - libusb_fill_control_transfer(ut.transfer, dev_handle, buf, transfer_callback, - &ut, timeout); -#endif err = libusb_submit_transfer(ut.transfer); if (!err) err = callback_wait(&ut, &transferred, timeout); - if (err == LIBUSB_TRANSFER_COMPLETED && transferred) { + if (err == LIBUSB_SUCCESS && transferred) { if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) memcpy(buffer, libusb_control_transfer_get_data(ut.transfer), transferred); err = transferred; goto out; } - if ((err) == LIBUSB_TRANSFER_CANCELLED) - err = LIBUSB_ERROR_TIMEOUT; + err = usb_transfer_toerr(err); out: complete_usb_transfer(&ut); return err; diff --git a/util.c b/util.c index 48c92251..36bced24 100644 --- a/util.c +++ b/util.c @@ -2410,7 +2410,7 @@ void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int l applog(LOG_WARNING, "Failed to read errno=%d" IN_FMT_FFL, errno, file, func, line); } -void _cgsem_destroy(cgsem_t *cgsem) +void cgsem_destroy(cgsem_t *cgsem) { close(cgsem->pipefd[1]); close(cgsem->pipefd[0]); @@ -2480,7 +2480,7 @@ int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, co return 0; } -void _cgsem_destroy(cgsem_t *cgsem) +void cgsem_destroy(cgsem_t *cgsem) { sem_destroy(cgsem); } @@ -2499,7 +2499,7 @@ void *completion_thread(void *arg) { struct cg_completion *cgc = (struct cg_completion *)arg; - pthread_detach(pthread_self()); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); cgc->fn(cgc->fnarg); cgsem_post(&cgc->cgsem); @@ -2522,7 +2522,10 @@ bool cg_completion_timeout(void *fn, void *fnarg, int timeout) pthread_create(&pthread, NULL, completion_thread, (void *)cgc); ret = cgsem_mswait(&cgc->cgsem, timeout); - if (!ret) + if (!ret) { + pthread_join(pthread, NULL); free(cgc); + } else + pthread_cancel(pthread); return !ret; } diff --git a/util.h b/util.h index fa4e7ed6..ae4a3456 100644 --- a/util.h +++ b/util.h @@ -134,14 +134,13 @@ void _cgsem_init(cgsem_t *cgsem, const char *file, const char *func, const int l void _cgsem_post(cgsem_t *cgsem, const char *file, const char *func, const int line); void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line); int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, const int line); -void _cgsem_destroy(cgsem_t *cgsem); +void cgsem_destroy(cgsem_t *cgsem); bool cg_completion_timeout(void *fn, void *fnarg, int timeout); #define cgsem_init(_sem) _cgsem_init(_sem, __FILE__, __func__, __LINE__) #define cgsem_post(_sem) _cgsem_post(_sem, __FILE__, __func__, __LINE__) #define cgsem_wait(_sem) _cgsem_wait(_sem, __FILE__, __func__, __LINE__) #define cgsem_mswait(_sem, _timeout) _cgsem_mswait(_sem, _timeout, __FILE__, __func__, __LINE__) -#define cgsem_destroy(_sem) _cgsem_destroy(_sem) /* Align a size_t to 4 byte boundaries for fussy arches */ static inline void align_len(size_t *len)