diff --git a/configure.ac b/configure.ac index f9db932c..a2860eb0 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ PKG_PROG_PKG_CONFIG() AC_CHECK_HEADERS(stdio.h stdlib.h fcntl.h sys/time.h unistd.h) AC_CHECK_HEADERS(ctype.h errno.h byteswap.h string.h time.h) AC_CHECK_HEADERS(endian.h sys/endian.h arpa/inet.h) -AC_CHECK_HEADERS(alloca.h pthread.h) +AC_CHECK_HEADERS(alloca.h pthread.h stdio.h) AC_CHECK_HEADERS(sys/types.h sys/socket.h) AC_CHECK_HEADERS(stdint.h netinet/in.h netinet/tcp.h) diff --git a/src/libckpool.c b/src/libckpool.c index ec07b449..493f2198 100644 --- a/src/libckpool.c +++ b/src/libckpool.c @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 Con Kolivas + * Copyright 2014 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,6 +23,200 @@ #include "ckpool.h" #include "libckpool.h" +/* Place holders for when we add lock debugging */ +#define GETLOCK(_lock, _file, _func, _line) +#define GOTLOCK(_lock, _file, _func, _line) +#define TRYLOCK(_lock, _file, _func, _line) +#define DIDLOCK(_ret, _lock, _file, _func, _line) +#define GUNLOCK(_lock, _file, _func, _line) +#define INITLOCK(_typ, _lock, _file, _func, _line) + +void _mutex_lock(pthread_mutex_t *lock, const char *file, const char *func, const int line) +{ + GETLOCK(lock, file, func, line); + if (unlikely(pthread_mutex_lock(lock))) + quitfrom(1, file, func, line, "WTF MUTEX ERROR ON LOCK! errno=%d", errno); + GOTLOCK(lock, file, func, line); +} + +void _mutex_unlock_noyield(pthread_mutex_t *lock, const char *file, const char *func, const int line) +{ + if (unlikely(pthread_mutex_unlock(lock))) + quitfrom(1, file, func, line, "WTF MUTEX ERROR ON UNLOCK! errno=%d", errno); + GUNLOCK(lock, file, func, line); +} + +void _mutex_unlock(pthread_mutex_t *lock, const char *file, const char *func, const int line) +{ + _mutex_unlock_noyield(lock, file, func, line); + sched_yield(); +} + +int _mutex_trylock(pthread_mutex_t *lock, __maybe_unused const char *file, __maybe_unused const char *func, __maybe_unused const int line) +{ + TRYLOCK(lock, file, func, line); + int ret = pthread_mutex_trylock(lock); + DIDLOCK(ret, lock, file, func, line); + return ret; +} + +void _wr_lock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) +{ + GETLOCK(lock, file, func, line); + if (unlikely(pthread_rwlock_wrlock(lock))) + quitfrom(1, file, func, line, "WTF WRLOCK ERROR ON LOCK! errno=%d", errno); + GOTLOCK(lock, file, func, line); +} + +int _wr_trylock(pthread_rwlock_t *lock, __maybe_unused const char *file, __maybe_unused const char *func, __maybe_unused const int line) +{ + TRYLOCK(lock, file, func, line); + int ret = pthread_rwlock_trywrlock(lock); + DIDLOCK(ret, lock, file, func, line); + return ret; +} + +void _rd_lock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) +{ + GETLOCK(lock, file, func, line); + if (unlikely(pthread_rwlock_rdlock(lock))) + quitfrom(1, file, func, line, "WTF RDLOCK ERROR ON LOCK! errno=%d", errno); + GOTLOCK(lock, file, func, line); +} + +void _rw_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) +{ + if (unlikely(pthread_rwlock_unlock(lock))) + quitfrom(1, file, func, line, "WTF RWLOCK ERROR ON UNLOCK! errno=%d", errno); + GUNLOCK(lock, file, func, line); +} + +void _rd_unlock_noyield(pthread_rwlock_t *lock, const char *file, const char *func, const int line) +{ + _rw_unlock(lock, file, func, line); +} + +void _wr_unlock_noyield(pthread_rwlock_t *lock, const char *file, const char *func, const int line) +{ + _rw_unlock(lock, file, func, line); +} + +void _rd_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) +{ + _rw_unlock(lock, file, func, line); + sched_yield(); +} + +void _wr_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line) +{ + _rw_unlock(lock, file, func, line); + sched_yield(); +} + +void _mutex_init(pthread_mutex_t *lock, const char *file, const char *func, const int line) +{ + if (unlikely(pthread_mutex_init(lock, NULL))) + quitfrom(1, file, func, line, "Failed to pthread_mutex_init errno=%d", errno); + INITLOCK(lock, CGLOCK_MUTEX, file, func, line); +} + +void mutex_destroy(pthread_mutex_t *lock) +{ + /* Ignore return code. This only invalidates the mutex on linux but + * releases resources on windows. */ + pthread_mutex_destroy(lock); +} + +void _rwlock_init(pthread_rwlock_t *lock, const char *file, const char *func, const int line) +{ + if (unlikely(pthread_rwlock_init(lock, NULL))) + quitfrom(1, file, func, line, "Failed to pthread_rwlock_init errno=%d", errno); + INITLOCK(lock, CGLOCK_RW, file, func, line); +} + +void rwlock_destroy(pthread_rwlock_t *lock) +{ + pthread_rwlock_destroy(lock); +} + +void _cklock_init(cklock_t *lock, const char *file, const char *func, const int line) +{ + _mutex_init(&lock->mutex, file, func, line); + _rwlock_init(&lock->rwlock, file, func, line); +} + +void cklock_destroy(cklock_t *lock) +{ + rwlock_destroy(&lock->rwlock); + mutex_destroy(&lock->mutex); +} + +/* Read lock variant of cklock. Cannot be promoted. */ +void _ck_rlock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _mutex_lock(&lock->mutex, file, func, line); + _rd_lock(&lock->rwlock, file, func, line); + _mutex_unlock_noyield(&lock->mutex, file, func, line); +} + +/* Intermediate variant of cklock - behaves as a read lock but can be promoted + * to a write lock or demoted to read lock. */ +void _ck_ilock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _mutex_lock(&lock->mutex, file, func, line); +} + +/* Unlock intermediate variant without changing to read or write version */ +void _ck_uilock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _mutex_unlock(&lock->mutex, file, func, line); +} + +/* Upgrade intermediate variant to a write lock */ +void _ck_ulock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _wr_lock(&lock->rwlock, file, func, line); +} + +/* Write lock variant of cklock */ +void _ck_wlock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _mutex_lock(&lock->mutex, file, func, line); + _wr_lock(&lock->rwlock, file, func, line); +} + +/* Downgrade write variant to a read lock */ +void _ck_dwlock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _wr_unlock_noyield(&lock->rwlock, file, func, line); + _rd_lock(&lock->rwlock, file, func, line); + _mutex_unlock_noyield(&lock->mutex, file, func, line); +} + +/* Demote a write variant to an intermediate variant */ +void _ck_dwilock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _wr_unlock(&lock->rwlock, file, func, line); +} + +/* Downgrade intermediate variant to a read lock */ +void _ck_dlock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _rd_lock(&lock->rwlock, file, func, line); + _mutex_unlock_noyield(&lock->mutex, file, func, line); +} + +void _ck_runlock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _rd_unlock(&lock->rwlock, file, func, line); +} + +void _ck_wunlock(cklock_t *lock, const char *file, const char *func, const int line) +{ + _wr_unlock_noyield(&lock->rwlock, file, func, line); + _mutex_unlock(&lock->mutex, file, func, line); +} + void keep_sockalive(int fd) { const int tcp_one = 1; diff --git a/src/libckpool.h b/src/libckpool.h index d0feeec2..8a19018a 100644 --- a/src/libckpool.h +++ b/src/libckpool.h @@ -14,6 +14,81 @@ #include #include +#include +#include + +#define mutex_lock(_lock) _mutex_lock(_lock, __FILE__, __func__, __LINE__) +#define mutex_unlock_noyield(_lock) _mutex_unlock_noyield(_lock, __FILE__, __func__, __LINE__) +#define mutex_unlock(_lock) _mutex_unlock(_lock, __FILE__, __func__, __LINE__) +#define mutex_trylock(_lock) _mutex_trylock(_lock, __FILE__, __func__, __LINE__) +#define wr_lock(_lock) _wr_lock(_lock, __FILE__, __func__, __LINE__) +#define wr_trylock(_lock) _wr_trylock(_lock, __FILE__, __func__, __LINE__) +#define rd_lock(_lock) _rd_lock(_lock, __FILE__, __func__, __LINE__) +#define rw_unlock(_lock) _rw_unlock(_lock, __FILE__, __func__, __LINE__) +#define rd_unlock_noyield(_lock) _rd_unlock_noyield(_lock, __FILE__, __func__, __LINE__) +#define wr_unlock_noyield(_lock) _wr_unlock_noyield(_lock, __FILE__, __func__, __LINE__) +#define rd_unlock(_lock) _rd_unlock(_lock, __FILE__, __func__, __LINE__) +#define wr_unlock(_lock) _wr_unlock(_lock, __FILE__, __func__, __LINE__) +#define mutex_init(_lock) _mutex_init(_lock, __FILE__, __func__, __LINE__) +#define rwlock_init(_lock) _rwlock_init(_lock, __FILE__, __func__, __LINE__) +#define cklock_init(_lock) _cklock_init(_lock, __FILE__, __func__, __LINE__) +#define ck_rlock(_lock) _ck_rlock(_lock, __FILE__, __func__, __LINE__) +#define ck_ilock(_lock) _ck_ilock(_lock, __FILE__, __func__, __LINE__) +#define ck_uilock(_lock) _ck_uilock(_lock, __FILE__, __func__, __LINE__) +#define ck_ulock(_lock) _ck_ulock(_lock, __FILE__, __func__, __LINE__) +#define ck_wlock(_lock) _ck_wlock(_lock, __FILE__, __func__, __LINE__) +#define ck_dwlock(_lock) _ck_dwlock(_lock, __FILE__, __func__, __LINE__) +#define ck_dwilock(_lock) _ck_dwilock(_lock, __FILE__, __func__, __LINE__) +#define ck_dlock(_lock) _ck_dlock(_lock, __FILE__, __func__, __LINE__) +#define ck_runlock(_lock) _ck_runlock(_lock, __FILE__, __func__, __LINE__) +#define ck_wunlock(_lock) _ck_wunlock(_lock, __FILE__, __func__, __LINE__) + +#define IN_FMT_FFL " in %s %s():%d" +#define quitfrom(status, _file, _func, _line, fmt, ...) do { \ + if (fmt) { \ + fprintf(stderr, fmt IN_FMT_FFL, ##__VA_ARGS__, _file, _func, _line); \ + fprintf(stderr, "\n"); \ + fflush(stderr); \ + } \ + exit(status); \ +} while (0) + +/* cgminer locks, a write biased variant of rwlocks */ +struct cklock { + pthread_mutex_t mutex; + pthread_rwlock_t rwlock; +}; + +typedef struct cklock cklock_t; + +void _mutex_lock(pthread_mutex_t *lock, const char *file, const char *func, const int line); +void _mutex_unlock_noyield(pthread_mutex_t *lock, const char *file, const char *func, const int line); +void _mutex_unlock(pthread_mutex_t *lock, const char *file, const char *func, const int line); +int _mutex_trylock(pthread_mutex_t *lock, __maybe_unused const char *file, __maybe_unused const char *func, __maybe_unused const int line); +void _wr_lock(pthread_rwlock_t *lock, const char *file, const char *func, const int line); +int _wr_trylock(pthread_rwlock_t *lock, __maybe_unused const char *file, __maybe_unused const char *func, __maybe_unused const int line); +void _rd_lock(pthread_rwlock_t *lock, const char *file, const char *func, const int line); +void _rw_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line); +void _rd_unlock_noyield(pthread_rwlock_t *lock, const char *file, const char *func, const int line); +void _wr_unlock_noyield(pthread_rwlock_t *lock, const char *file, const char *func, const int line); +void _rd_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line); +void _wr_unlock(pthread_rwlock_t *lock, const char *file, const char *func, const int line); +void _mutex_init(pthread_mutex_t *lock, const char *file, const char *func, const int line); +void mutex_destroy(pthread_mutex_t *lock); +void _rwlock_init(pthread_rwlock_t *lock, const char *file, const char *func, const int line); +void rwlock_destroy(pthread_rwlock_t *lock); +void _cklock_init(cklock_t *lock, const char *file, const char *func, const int line); +void cklock_destroy(cklock_t *lock); +void _ck_rlock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_ilock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_uilock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_ulock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_wlock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_dwlock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_dwilock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_dlock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_runlock(cklock_t *lock, const char *file, const char *func, const int line); +void _ck_wunlock(cklock_t *lock, const char *file, const char *func, const int line); static inline bool sock_connecting(void) {