aboutsummaryrefslogtreecommitdiff
path: root/cpu-miner.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpu-miner.c')
-rw-r--r--cpu-miner.c287
1 files changed, 243 insertions, 44 deletions
diff --git a/cpu-miner.c b/cpu-miner.c
index fd64a0c..a78be29 100644
--- a/cpu-miner.c
+++ b/cpu-miner.c
@@ -4,7 +4,7 @@
*
* 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
- * Software Foundation; either version 2 of the License, or (at your option)
+ * Software Foundation; either version 2 of the License, or (at your option)
* any later version. See COPYING for more details.
*/
@@ -32,6 +32,25 @@
#define DEF_RPC_URL "http://127.0.0.1:8332/"
#define DEF_RPC_USERPASS "rpcuser:rpcpass"
+struct thr_info {
+ int id;
+ pthread_t pth;
+ struct thread_q *q;
+};
+
+enum workio_commands {
+ WC_GET_WORK,
+ WC_SUBMIT_WORK,
+};
+
+struct workio_cmd {
+ enum workio_commands cmd;
+ struct thr_info *thr;
+ union {
+ struct work *work;
+ } u;
+};
+
enum sha256_algos {
ALGO_C, /* plain C */
ALGO_4WAY, /* parallel SSE2 */
@@ -70,6 +89,8 @@ static enum sha256_algos opt_algo = ALGO_C;
static int opt_n_threads = 1;
static char *rpc_url;
static char *userpass;
+static struct thr_info *thr_info;
+static int work_thr_id;
struct option_help {
@@ -214,20 +235,21 @@ err_out:
return false;
}
-static void submit_work(CURL *curl, struct work *work)
+static bool submit_upstream_work(CURL *curl, const struct work *work)
{
char *hexstr = NULL;
json_t *val, *res;
char s[345], timestr[64];
time_t now;
struct tm *tm;
+ bool rc = false;
now = time(NULL);
/* build hex string */
hexstr = bin2hex(work->data, sizeof(work->data));
if (!hexstr) {
- fprintf(stderr, "submit_work OOM\n");
+ fprintf(stderr, "submit_upstream_work OOM\n");
goto out;
}
@@ -242,7 +264,7 @@ static void submit_work(CURL *curl, struct work *work)
/* issue JSON-RPC request */
val = json_rpc_call(curl, rpc_url, userpass, s);
if (!val) {
- fprintf(stderr, "submit_work json_rpc_call failed\n");
+ fprintf(stderr, "submit_upstream_work json_rpc_call failed\n");
goto out;
}
@@ -256,11 +278,14 @@ static void submit_work(CURL *curl, struct work *work)
json_decref(val);
+ rc = true;
+
out:
free(hexstr);
+ return rc;
}
-static bool get_work(CURL *curl, struct work *work)
+static bool get_upstream_work(CURL *curl, struct work *work)
{
static const char *rpc_req =
"{\"method\": \"getwork\", \"params\": [], \"id\":0}\r\n";
@@ -278,6 +303,120 @@ static bool get_work(CURL *curl, struct work *work)
return rc;
}
+static void workio_cmd_free(struct workio_cmd *wc)
+{
+ if (!wc)
+ return;
+
+ switch (wc->cmd) {
+ case WC_SUBMIT_WORK:
+ free(wc->u.work);
+ break;
+ default: /* do nothing */
+ break;
+ }
+
+ memset(wc, 0, sizeof(*wc)); /* poison */
+ free(wc);
+}
+
+static bool workio_get_work(struct workio_cmd *wc, CURL *curl)
+{
+ struct work *ret_work;
+ int failures = 0;
+
+ ret_work = calloc(1, sizeof(*ret_work));
+ if (!ret_work)
+ return false;
+
+ /* obtain new work from bitcoin via JSON-RPC */
+ while (!get_upstream_work(curl, ret_work)) {
+ fprintf(stderr, "json_rpc_call failed, ");
+
+ if ((opt_retries >= 0) && (++failures > opt_retries)) {
+ fprintf(stderr, "terminating workio thread\n");
+ free(ret_work);
+ return false;
+ }
+
+ /* pause, then restart work-request loop */
+ fprintf(stderr, "retry after %d seconds\n",
+ opt_fail_pause);
+ sleep(opt_fail_pause);
+ }
+
+ /* send work to requesting thread */
+ if (!tq_push(wc->thr->q, ret_work))
+ free(ret_work);
+
+ return true;
+}
+
+static bool workio_submit_work(struct workio_cmd *wc, CURL *curl)
+{
+ int failures = 0;
+
+ /* submit solution to bitcoin via JSON-RPC */
+ while (!submit_upstream_work(curl, wc->u.work)) {
+ if ((opt_retries >= 0) && (++failures > opt_retries)) {
+ fprintf(stderr, "...terminating workio thread\n");
+ return false;
+ }
+
+ /* pause, then restart work-request loop */
+ fprintf(stderr, "...retry after %d seconds\n",
+ opt_fail_pause);
+ sleep(opt_fail_pause);
+ }
+
+ return true;
+}
+
+static void *workio_thread(void *userdata)
+{
+ struct thr_info *mythr = userdata;
+ CURL *curl;
+ bool ok = true;
+
+ curl = curl_easy_init();
+ if (!curl) {
+ fprintf(stderr, "CURL initialization failed\n");
+ return NULL;
+ }
+
+ while (ok) {
+ struct workio_cmd *wc;
+
+ /* wait for workio_cmd sent to us, on our queue */
+ wc = tq_pop(mythr->q, NULL);
+ if (!wc) {
+ ok = false;
+ break;
+ }
+
+ /* process workio_cmd */
+ switch (wc->cmd) {
+ case WC_GET_WORK:
+ ok = workio_get_work(wc, curl);
+ break;
+ case WC_SUBMIT_WORK:
+ ok = workio_submit_work(wc, curl);
+ break;
+
+ default: /* should never happen */
+ ok = false;
+ break;
+ }
+
+ workio_cmd_free(wc);
+ }
+
+ tq_freeze(mythr->q);
+ curl_easy_cleanup(curl);
+
+ return NULL;
+}
+
static void hashmeter(int thr_id, const struct timeval *diff,
unsigned long hashes_done)
{
@@ -292,39 +431,82 @@ static void hashmeter(int thr_id, const struct timeval *diff,
khashes / secs);
}
-static void *miner_thread(void *thr_id_int)
+static bool get_work(struct thr_info *thr, struct work *work)
{
- int thr_id = (unsigned long) thr_id_int;
- int failures = 0;
- uint32_t max_nonce = 0xffffff;
- CURL *curl;
+ struct workio_cmd *wc;
+ struct work *work_heap;
- curl = curl_easy_init();
- if (!curl) {
- fprintf(stderr, "CURL initialization failed\n");
- return NULL;
+ /* fill out work request message */
+ wc = calloc(1, sizeof(*wc));
+ if (!wc)
+ return false;
+
+ wc->cmd = WC_GET_WORK;
+ wc->thr = thr;
+
+ /* send work request to workio thread */
+ if (!tq_push(thr_info[work_thr_id].q, wc)) {
+ workio_cmd_free(wc);
+ return false;
}
+ /* wait for response, a unit of work */
+ work_heap = tq_pop(thr->q, NULL);
+ if (!work_heap)
+ return false;
+
+ /* copy returned work into storage provided by caller */
+ memcpy(work, work_heap, sizeof(*work));
+ free(work_heap);
+
+ return true;
+}
+
+static bool submit_work(struct thr_info *thr, const struct work *work_in)
+{
+ struct workio_cmd *wc;
+
+ /* fill out work request message */
+ wc = calloc(1, sizeof(*wc));
+ if (!wc)
+ return false;
+
+ wc->u.work = malloc(sizeof(*work_in));
+ if (!wc->u.work)
+ goto err_out;
+
+ wc->cmd = WC_SUBMIT_WORK;
+ wc->thr = thr;
+ memcpy(wc->u.work, work_in, sizeof(*work_in));
+
+ /* send solution to workio thread */
+ if (!tq_push(thr_info[work_thr_id].q, wc))
+ goto err_out;
+
+ return true;
+
+err_out:
+ workio_cmd_free(wc);
+ return false;
+}
+
+static void *miner_thread(void *userdata)
+{
+ struct thr_info *mythr = userdata;
+ int thr_id = mythr->id;
+ uint32_t max_nonce = 0xffffff;
+
while (1) {
struct work work __attribute__((aligned(128)));
unsigned long hashes_done;
struct timeval tv_start, tv_end, diff;
bool rc;
- /* obtain new work from bitcoin */
- if (!get_work(curl, &work)) {
- fprintf(stderr, "json_rpc_call failed, ");
-
- if ((opt_retries >= 0) && (++failures > opt_retries)) {
- fprintf(stderr, "terminating thread\n");
- return NULL; /* exit thread */
- }
-
- /* pause, then restart work loop */
- fprintf(stderr, "retry after %d seconds\n",
- opt_fail_pause);
- sleep(opt_fail_pause);
- continue;
+ /* obtain new work from internal workio thread */
+ if (!get_work(mythr, &work)) {
+ fprintf(stderr, "work retrieval failed, exiting "
+ "mining thread %d\n", mythr->id);
+ goto out;
}
hashes_done = 0;
@@ -347,7 +529,7 @@ static void *miner_thread(void *thr_id_int)
max_nonce, &hashes_done);
rc = (rc5 == -1) ? false : true;
}
- break;
+ break;
#endif
#ifdef WANT_SSE2_4WAY
@@ -384,7 +566,7 @@ static void *miner_thread(void *thr_id_int)
default:
/* should never happen */
- return NULL;
+ goto out;
}
/* record scanhash elapsed time */
@@ -404,13 +586,12 @@ static void *miner_thread(void *thr_id_int)
max_nonce += 100000; /* small increase */
/* if nonce found, submit work */
- if (rc)
- submit_work(curl, &work);
-
- failures = 0;
+ if (rc && !submit_work(mythr, &work))
+ break;
}
- curl_easy_cleanup(curl);
+out:
+ tq_freeze(mythr->q);
return NULL;
}
@@ -564,8 +745,8 @@ static void parse_cmdline(int argc, char *argv[])
int main (int argc, char *argv[])
{
+ struct thr_info *thr;
int i;
- pthread_t *t_all;
rpc_url = strdup(DEF_RPC_URL);
userpass = strdup(DEF_RPC_USERPASS);
@@ -577,14 +758,33 @@ int main (int argc, char *argv[])
if (setpriority(PRIO_PROCESS, 0, 19))
perror("setpriority");
- t_all = calloc(opt_n_threads, sizeof(pthread_t));
- if (!t_all)
+ thr_info = calloc(opt_n_threads + 1, sizeof(*thr));
+ if (!thr_info)
+ return 1;
+
+ work_thr_id = opt_n_threads;
+ thr = &thr_info[work_thr_id];
+ thr->id = opt_n_threads;
+ thr->q = tq_new();
+ if (!thr->q)
+ return 1;
+
+ /* start work I/O thread */
+ if (pthread_create(&thr->pth, NULL, workio_thread, thr)) {
+ fprintf(stderr, "workio thread create failed\n");
return 1;
+ }
/* start mining threads */
for (i = 0; i < opt_n_threads; i++) {
- if (pthread_create(&t_all[i], NULL, miner_thread,
- (void *)(unsigned long) i)) {
+ thr = &thr_info[i];
+
+ thr->id = i;
+ thr->q = tq_new();
+ if (!thr->q)
+ return 1;
+
+ if (pthread_create(&thr->pth, NULL, miner_thread, thr)) {
fprintf(stderr, "thread %d create failed\n", i);
return 1;
}
@@ -597,11 +797,10 @@ int main (int argc, char *argv[])
opt_n_threads,
algo_names[opt_algo]);
- /* main loop - simply wait for all threads to exit */
- for (i = 0; i < opt_n_threads; i++)
- pthread_join(t_all[i], NULL);
+ /* main loop - simply wait for workio thread to exit */
+ pthread_join(thr_info[work_thr_id].pth, NULL);
- fprintf(stderr, "all threads dead, fred. exiting.\n");
+ fprintf(stderr, "workio thread dead, exiting.\n");
return 0;
}