Browse Source

Merge branch 'master' into multiproxy

master
ckolivas 9 years ago
parent
commit
1c92bebc62
  1. 43
      pool/base.php
  2. 24
      pool/page_payout.php
  3. 6
      pool/page_usperf.php
  4. 22
      src/ckpool.c
  5. 40
      src/generator.c
  6. 65
      src/libckpool.c
  7. 5
      src/libckpool.h
  8. 11
      src/stratifier.c

43
pool/base.php

@ -99,7 +99,7 @@ function howlongago($sec)
return $des; return $des;
} }
# #
function howmanyhrs($tot, $days = false) function howmanyhrs($tot, $days = false, $dh = false)
{ {
$sec = round($tot); $sec = round($tot);
if ($sec < 60) if ($sec < 60)
@ -118,10 +118,47 @@ function howmanyhrs($tot, $days = false)
{ {
$dy = floor($hr / 24); $dy = floor($hr / 24);
$hr -= $dy * 24; $hr -= $dy * 24;
$des = $dy.'d '.$hr.'hr '.$min.'m '.$sec.'s'; if ($dh == true)
{
if ($min >= 30)
{
$hr++;
if ($hr > 23)
{
$dy++;
$hr -= 24;
}
}
$ds = '';
if ($dy != 1)
$ds = 's';
if ($hr == 0)
$des = "${dy}day$ds";
else
{
$hs = '';
if ($hr != 1)
$hs = 's';
$des = "${dy}day$ds ${hr}hr$hs";
}
}
else
$des = $dy.'d '.$hr.'hr '.$min.'m '.$sec.'s';
} }
else else
$des = $hr.'hr '.$min.'m '.$sec.'s'; {
if ($dh == true)
{
if ($min >= 30)
$hr++;
$hs = '';
if ($hr != 1)
$hs = 's';
$des = "${hr}hr$hs";
}
else
$des = $hr.'hr '.$min.'m '.$sec.'s';
}
} }
} }
return $des; return $des;

24
pool/page_payout.php

@ -14,7 +14,16 @@ function dopayout($data, $user)
if (isset($data['info']['currndiff'])) if (isset($data['info']['currndiff']))
$nd = $data['info']['currndiff']; $nd = $data['info']['currndiff'];
$nv = number_format($nd, 1); $nv = number_format($nd, 1);
$nvx = number_format($N*$nd, 1); $nvx = '<b>'.number_format($N*$nd, 1).'</b>';
$pd = $data['info']['p_hashrate24hr'];
$hr = 'is <b>?</b>';
$hrt = '<b>?</b>';
if ($pd != '?' && $pd != '' && $pd > 0)
{
$hr = 'for the last day is roughly <b>'.siprefmt($pd,2).'Hs</b>';
if ($nd > 0)
$hrt = '<b>'.howmanyhrs($nd * $N / ($pd / pow(2,32)), true, true).'</b>';
}
$pg = "<h1>Payouts</h1> $pg = "<h1>Payouts</h1>
<table width=75% cellpadding=0 cellspacing=0 border=0><tr><td> <table width=75% cellpadding=0 cellspacing=0 border=0><tr><td>
@ -28,7 +37,16 @@ The $n1 value the pool uses is $t times the network difficulty when the block is
Transaction fees are included in the miner reward.<br> Transaction fees are included in the miner reward.<br>
Pool fee is 0.9% of the total.<br><br> Pool fee is 0.9% of the total.<br><br>
<span class=hdr>How do the payments work?</span><br><br> <span class=hdr>PPL${n1}S acts like the reward 'ramps up' when you first start mining.<br>What actually happens?</span><br><br>
The $n means it takes that long to reward your shares.<br>
The ramp isn't missing rewards, it's delaying them to reduce variance.<br>
Each share is rewarded in all the blocks found in the $n after the share.<br>
That's simply how it reduces variance. Each share's reward is averaged out over the $n after it.<br>
The pool hash rate $hr which means the $n 'ramp' is roughly $hrt.<br><br>
Continue reading below for more detail about how it works:<br><br>
<span class=hdr>How do the <b>PPL${n1}S</b> payments work?</span><br><br>
The $n means the pool rewards $t times the expected number of shares, each time a block is found.<br> The $n means the pool rewards $t times the expected number of shares, each time a block is found.<br>
So each share will be paid approximately $ot of it's expected value, in each block it gets a reward,<br> So each share will be paid approximately $ot of it's expected value, in each block it gets a reward,<br>
but each share is also expected, on average, to be rewarded $t times in blocks found after the share is submitted to the pool.<br> but each share is also expected, on average, to be rewarded $t times in blocks found after the share is submitted to the pool.<br>
@ -48,7 +66,7 @@ A ckpool restart will also end the current shift and start a new shift.<br>
A network difficulty change will also end the current shift and start a new shift.<br><br> A network difficulty change will also end the current shift and start a new shift.<br><br>
<span class=hdr>So, what's the $n value?</span><br><br> <span class=hdr>So, what's the $n value?</span><br><br>
The current Bitcoin network value for $n1d is $nv and thus $n is <b>$nvx</b><br> The current Bitcoin network value for $n1d is $nv and thus $n is $nvx<br>
Bitcoin adjusts the $n1d value every 2016 blocks, which is about every 2 weeks.<br><br> Bitcoin adjusts the $n1d value every 2016 blocks, which is about every 2 weeks.<br><br>
When a block is found, the reward process counts back shifts until the total share difficulty included is $n.<br> When a block is found, the reward process counts back shifts until the total share difficulty included is $n.<br>
Since shares are summarised into shifts, it will include the full shift at the end of the range counting backwards,<br> Since shares are summarised into shifts, it will include the full shift at the end of the range counting backwards,<br>

6
pool/page_usperf.php

@ -1,8 +1,8 @@
<?php <?php
# #
function uspg($nc,$fs) function uspg($nc,$ff,$fs)
{ {
$g = "function exf(n,c,xn,x0,x1,y0,y1,ar){if(n==0){var i,st=-1,fi=-1;for(i=0;i<xn;i++){if(st==-1&&ar['lastpayoutstart:'+i]!=''){st=ar['start:'+i]}if(fi==-1&&ar['endmarkextra:'+i].indexOf('Block')==0){fi=ar['end:'+i]}}if(st>=0&&fi>=0){var xs,xf;xs=(st-x0)/(x1-x0);xf=(fi-x0)/(x1-x0);gfs(c,'$fs');gbe(c,xs,0);gln(c,xs,1);gln(c,xf,1);gln(c,xf,0);gle(c);gfl(c)}}} $g = "function exf(n,c,xn,x0,x1,y0,y1,ar){if(n==0){var i,st=-1,fi=-1;for(i=0;i<xn;i++){if(st==-1&&ar['lastpayoutstart:'+i]!=''){st=ar['start:'+i]}if(fi==-1&&ar['endmarkextra:'+i].indexOf('Block')==0){fi=ar['end:'+i]}}if(st>=0&&fi>=0){var xs,xf;xs=(st-x0)/(x1-x0);xf=(fi-x0)/(x1-x0);gfs(c,'$fs');gbe(c,xs,0);gln(c,xs,1);gln(c,xf,1);gln(c,xf,0);gle(c);gfl(c);gfz(c,xs,1,0,2,'Last 5Nd Reward \u279c','$ff','left')}}}
function gdrw(c,d,cbx){gc(c);ghrs(c);gopt(c,cbx); function gdrw(c,d,cbx){gc(c);ghrs(c);gopt(c,cbx);
gfs(c,'white');gss(c,'#0000c0');glw(c,2);gbd(c); gfs(c,'white');gss(c,'#0000c0');glw(c,2);gbd(c);
var rows=d['rows'],ymin=-1,ymax=0,xmin=-1,xmax=0,tda=[]; var rows=d['rows'],ymin=-1,ymax=0,xmin=-1,xmax=0,tda=[];
@ -110,7 +110,7 @@ function dousperf($data, $user)
$data = str_replace(array("\\","'"), array("\\\\","\\'"), $ans['DATA']); $data = str_replace(array("\\","'"), array("\\\\","\\'"), $ans['DATA']);
$data .= $fld_sep . 'cols' . $val_sep . $datacols; $data .= $fld_sep . 'cols' . $val_sep . $datacols;
$pg .= "<script type='text/javascript'>\n"; $pg .= "<script type='text/javascript'>\n";
$pg .= uspg($nc,'#fff0f0'); $pg .= uspg($nc,'#ff0000','#fff0f0');
$pg .= "\nfunction godrw(f){var cbx=["; $pg .= "\nfunction godrw(f){var cbx=[";
$comma = ''; $comma = '';
foreach ($cbx as $nam => $txt) foreach ($cbx as $nam => $txt)

22
src/ckpool.c

@ -543,7 +543,7 @@ rewait:
ret = 0; ret = 0;
goto out; goto out;
} }
ret = wait_recv_select(cs->fd, eom ? 0 : *timeout); ret = wait_read_select(cs->fd, eom ? 0 : *timeout);
polled = true; polled = true;
if (ret < 1) { if (ret < 1) {
if (!ret) { if (!ret) {
@ -616,7 +616,6 @@ out:
if (ret < 0) { if (ret < 0) {
empty_buffer(cs); empty_buffer(cs);
dealloc(cs->buf); dealloc(cs->buf);
Close(cs->fd);
} }
return ret; return ret;
} }
@ -763,6 +762,8 @@ static const char *rpc_method(const char *rpc_req)
return rpc_req; return rpc_req;
} }
/* All of these calls are made to bitcoind which prefers open/close instead
* of persistent connections so cs->fd is always invalid. */
json_t *json_rpc_call(connsock_t *cs, const char *rpc_req) json_t *json_rpc_call(connsock_t *cs, const char *rpc_req)
{ {
float timeout = RPC_TIMEOUT; float timeout = RPC_TIMEOUT;
@ -775,8 +776,9 @@ json_t *json_rpc_call(connsock_t *cs, const char *rpc_req)
/* Serialise all calls in case we use cs from multiple threads */ /* Serialise all calls in case we use cs from multiple threads */
cksem_wait(&cs->sem); cksem_wait(&cs->sem);
cs->fd = connect_socket(cs->url, cs->port);
if (unlikely(cs->fd < 0)) { if (unlikely(cs->fd < 0)) {
LOGWARNING("FD %d invalid in %s", cs->fd, __func__); LOGWARNING("Unable to connect socket to %s:%s in %s", cs->url, cs->port, __func__);
goto out; goto out;
} }
if (unlikely(!cs->url)) { if (unlikely(!cs->url)) {
@ -859,20 +861,8 @@ json_t *json_rpc_call(connsock_t *cs, const char *rpc_req)
out_empty: out_empty:
empty_socket(cs->fd); empty_socket(cs->fd);
empty_buffer(cs); empty_buffer(cs);
if (!val) {
/* Assume that a failed request means the socket will be closed
* and reopen it */
Close(cs->fd);
}
out: out:
if (cs->fd < 0) { Close(cs->fd);
/* Attempt to reopen a socket that has been closed due to a
* failed request or if the socket was closed while trying to
* read/write to it. */
cs->fd = connect_socket(cs->url, cs->port);
LOGWARNING("Attempt to reopen socket to %s:%s %ssuccessful",
cs->url, cs->port, cs->fd > 0 ? "" : "un");
}
free(http_req); free(http_req);
dealloc(cs->buf); dealloc(cs->buf);
cksem_post(&cs->sem); cksem_post(&cs->sem);

40
src/generator.c

@ -166,18 +166,18 @@ struct generator_data {
typedef struct generator_data gdata_t; typedef struct generator_data gdata_t;
/* Use a temporary fd when testing server_alive to avoid races on cs->fd */
static bool server_alive(ckpool_t *ckp, server_instance_t *si, bool pinging) static bool server_alive(ckpool_t *ckp, server_instance_t *si, bool pinging)
{ {
char *userpass = NULL; char *userpass = NULL;
bool ret = false; bool ret = false;
connsock_t *cs; connsock_t *cs;
gbtbase_t *gbt; gbtbase_t *gbt;
int fd;
cs = &si->cs; if (si->alive)
/* Has this server already been reconnected? */
if (cs->fd > 0)
return true; return true;
si->alive = false; cs = &si->cs;
if (!extract_sockaddr(si->url, &cs->url, &cs->port)) { if (!extract_sockaddr(si->url, &cs->url, &cs->port)) {
LOGWARNING("Failed to extract address from %s", si->url); LOGWARNING("Failed to extract address from %s", si->url);
return ret; return ret;
@ -192,8 +192,8 @@ static bool server_alive(ckpool_t *ckp, server_instance_t *si, bool pinging)
return ret; return ret;
} }
cs->fd = connect_socket(cs->url, cs->port); fd = connect_socket(cs->url, cs->port);
if (cs->fd < 0) { if (fd < 0) {
if (!pinging) if (!pinging)
LOGWARNING("Failed to connect socket to %s:%s !", cs->url, cs->port); LOGWARNING("Failed to connect socket to %s:%s !", cs->url, cs->port);
return ret; return ret;
@ -214,16 +214,11 @@ static bool server_alive(ckpool_t *ckp, server_instance_t *si, bool pinging)
LOGWARNING("Invalid btcaddress: %s !", ckp->btcaddress); LOGWARNING("Invalid btcaddress: %s !", ckp->btcaddress);
goto out; goto out;
} }
ret = true; si->alive = ret = true;
LOGNOTICE("Server alive: %s:%s", cs->url, cs->port);
out: out:
if (!ret) { /* Close the file handle */
/* Close and invalidate the file handle */ close(fd);
Close(cs->fd);
} else {
si->alive = true;
LOGNOTICE("Server alive: %s:%s", cs->url, cs->port);
keep_sockalive(cs->fd);
}
return ret; return ret;
} }
@ -245,7 +240,7 @@ retry:
server_instance_t *si = ckp->servers[i]; server_instance_t *si = ckp->servers[i];
cs = &si->cs; cs = &si->cs;
if (si->alive && cs->fd > 0) { if (si->alive) {
alive = si; alive = si;
goto living; goto living;
} }
@ -340,7 +335,7 @@ retry:
} }
} while (!umsg); } while (!umsg);
if (unlikely(cs->fd < 0)) { if (unlikely(!si->alive)) {
LOGWARNING("%s:%s Bitcoind socket invalidated, will attempt failover", cs->url, cs->port); LOGWARNING("%s:%s Bitcoind socket invalidated, will attempt failover", cs->url, cs->port);
goto reconnect; goto reconnect;
} }
@ -355,6 +350,7 @@ retry:
if (!gen_gbtbase(cs, gbt)) { if (!gen_gbtbase(cs, gbt)) {
LOGWARNING("Failed to get block template from %s:%s", LOGWARNING("Failed to get block template from %s:%s",
cs->url, cs->port); cs->url, cs->port);
si->alive = false;
send_unix_msg(umsg->sockd, "Failed"); send_unix_msg(umsg->sockd, "Failed");
goto reconnect; goto reconnect;
} else { } else {
@ -370,6 +366,7 @@ retry:
else if (!get_bestblockhash(cs, hash)) { else if (!get_bestblockhash(cs, hash)) {
LOGINFO("No best block hash support from %s:%s", LOGINFO("No best block hash support from %s:%s",
cs->url, cs->port); cs->url, cs->port);
si->alive = false;
send_unix_msg(umsg->sockd, "failed"); send_unix_msg(umsg->sockd, "failed");
} else { } else {
send_unix_msg(umsg->sockd, hash); send_unix_msg(umsg->sockd, hash);
@ -380,11 +377,13 @@ retry:
if (si->notify) if (si->notify)
send_unix_msg(umsg->sockd, "notify"); send_unix_msg(umsg->sockd, "notify");
else if ((height = get_blockcount(cs)) == -1) { else if ((height = get_blockcount(cs)) == -1) {
si->alive = false;
send_unix_msg(umsg->sockd, "failed"); send_unix_msg(umsg->sockd, "failed");
goto reconnect; goto reconnect;
} else { } else {
LOGDEBUG("Height: %d", height); LOGDEBUG("Height: %d", height);
if (!get_blockhash(cs, height, hash)) { if (!get_blockhash(cs, height, hash)) {
si->alive = false;
send_unix_msg(umsg->sockd, "failed"); send_unix_msg(umsg->sockd, "failed");
goto reconnect; goto reconnect;
} else { } else {
@ -2766,6 +2765,8 @@ static int server_mode(ckpool_t *ckp, proc_instance_t *pi)
ckp->servers = ckalloc(sizeof(server_instance_t *) * ckp->btcds); ckp->servers = ckalloc(sizeof(server_instance_t *) * ckp->btcds);
for (i = 0; i < ckp->btcds; i++) { for (i = 0; i < ckp->btcds; i++) {
connsock_t *cs;
ckp->servers[i] = ckzalloc(sizeof(server_instance_t)); ckp->servers[i] = ckzalloc(sizeof(server_instance_t));
si = ckp->servers[i]; si = ckp->servers[i];
si->url = ckp->btcdurl[i]; si->url = ckp->btcdurl[i];
@ -2773,8 +2774,9 @@ static int server_mode(ckpool_t *ckp, proc_instance_t *pi)
si->pass = ckp->btcdpass[i]; si->pass = ckp->btcdpass[i];
si->notify = ckp->btcdnotify[i]; si->notify = ckp->btcdnotify[i];
si->id = i; si->id = i;
cksem_init(&si->cs.sem); cs = &si->cs;
cksem_post(&si->cs.sem); cksem_init(&cs->sem);
cksem_post(&cs->sem);
} }
create_pthread(&pth_watchdog, server_watchdog, ckp); create_pthread(&pth_watchdog, server_watchdog, ckp);

65
src/libckpool.c

@ -16,6 +16,7 @@
#else #else
#include <sys/un.h> #include <sys/un.h>
#endif #endif
#include <sys/epoll.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -905,46 +906,18 @@ int wait_close(int sockd, int timeout)
return sfd.revents & (POLLHUP | POLLRDHUP | POLLERR); return sfd.revents & (POLLHUP | POLLRDHUP | POLLERR);
} }
/* Emulate a select read wait for high fds that select doesn't support. /* Emulate a select read wait for high fds that select doesn't support. */
* wait_read_select is for unix sockets and _wait_recv_select for regular int wait_read_select(int sockd, float timeout)
* sockets. */
int _wait_read_select(int *sockd, float timeout)
{ {
struct pollfd sfd; struct epoll_event event;
int ret = -1; int epfd, ret;
if (unlikely(*sockd < 0)) epfd = epoll_create1(EPOLL_CLOEXEC);
goto out; event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
sfd.fd = *sockd; epoll_ctl(epfd, EPOLL_CTL_ADD, sockd, &event);
sfd.events = POLLIN | POLLRDHUP;
timeout *= 1000; timeout *= 1000;
ret = poll(&sfd, 1, timeout); ret = epoll_wait(epfd, &event, 1, timeout);
if (ret > 0 && sfd.revents & (POLLERR)) { close(epfd);
ret = -1;
_Close(sockd);
}
out:
return ret;
}
int _wait_recv_select(int *sockd, float timeout)
{
struct pollfd sfd;
int ret = -1;
if (unlikely(*sockd < 0))
goto out;
sfd.fd = *sockd;
sfd.events = POLLIN | POLLRDHUP;
timeout *= 1000;
ret = poll(&sfd, 1, timeout);
/* If POLLRDHUP occurs, we may still have data to read so let recv()
* after this determine if the socket can still be used. */
if (ret > 0 && sfd.revents & (POLLHUP | POLLERR)) {
ret = -1;
_Close(sockd);
}
out:
return ret; return ret;
} }
@ -1018,19 +991,15 @@ out:
/* Emulate a select write wait for high fds that select doesn't support */ /* Emulate a select write wait for high fds that select doesn't support */
int wait_write_select(int sockd, float timeout) int wait_write_select(int sockd, float timeout)
{ {
struct pollfd sfd; struct epoll_event event;
int ret = -1; int epfd, ret;
if (unlikely(sockd < 0)) epfd = epoll_create1(EPOLL_CLOEXEC);
goto out; event.events = EPOLLOUT | EPOLLRDHUP | EPOLLONESHOT;
sfd.fd = sockd; epoll_ctl(epfd, EPOLL_CTL_ADD, sockd, &event);
sfd.events = POLLOUT | POLLRDHUP;
sfd.revents = 0;
timeout *= 1000; timeout *= 1000;
ret = poll(&sfd, 1, timeout); ret = epoll_wait(epfd, &event, 1, timeout);
if (ret && !(sfd.revents & POLLOUT)) close(epfd);
ret = -1;
out:
return ret; return ret;
} }

5
src/libckpool.h

@ -496,10 +496,7 @@ int _open_unix_server(const char *server_path, const char *file, const char *fun
int _open_unix_client(const char *server_path, const char *file, const char *func, const int line); int _open_unix_client(const char *server_path, const char *file, const char *func, const int line);
#define open_unix_client(server_path) _open_unix_client(server_path, __FILE__, __func__, __LINE__) #define open_unix_client(server_path) _open_unix_client(server_path, __FILE__, __func__, __LINE__)
int wait_close(int sockd, int timeout); int wait_close(int sockd, int timeout);
int _wait_read_select(int *sockd, float timeout); int wait_read_select(int sockd, float timeout);
#define wait_read_select(SOCKD, TIMEOUT) _wait_read_select(&(SOCKD), TIMEOUT)
int _wait_recv_select(int *sockd, float timeout);
#define wait_recv_select(SOCKD, TIMEOUT) _wait_recv_select(&(SOCKD), TIMEOUT)
int read_length(int sockd, void *buf, int len); int read_length(int sockd, void *buf, int len);
char *_recv_unix_msg(int sockd, int timeout1, int timeout2, const char *file, const char *func, const int line); char *_recv_unix_msg(int sockd, int timeout1, int timeout2, const char *file, const char *func, const int line);
#define RECV_UNIX_TIMEOUT1 30 #define RECV_UNIX_TIMEOUT1 30

11
src/stratifier.c

@ -983,6 +983,8 @@ static void *do_update(void *arg)
pthread_detach(pthread_self()); pthread_detach(pthread_self());
rename_proc("updater"); rename_proc("updater");
/* Serialise access to getbase to avoid out of order new block notifies */
cksem_wait(&sdata->update_sem);
retry: retry:
buf = send_recv_generator(ckp, "getbase", prio); buf = send_recv_generator(ckp, "getbase", prio);
if (unlikely(!buf)) { if (unlikely(!buf)) {
@ -994,6 +996,8 @@ retry:
LOGWARNING("Generator returned failure in update_base, retry #%d", retries); LOGWARNING("Generator returned failure in update_base, retry #%d", retries);
goto retry; goto retry;
} }
LOGWARNING("Generator failed in update_base after retrying");
goto out;
} }
if (unlikely(retries)) if (unlikely(retries))
LOGWARNING("Generator succeeded in update_base after retrying"); LOGWARNING("Generator succeeded in update_base after retrying");
@ -1036,8 +1040,6 @@ retry:
json_decref(val); json_decref(val);
generate_coinbase(ckp, wb); generate_coinbase(ckp, wb);
/* Serialise access to add_base to avoid out of order new block notifies */
cksem_wait(&sdata->update_sem);
add_base(ckp, sdata, wb, &new_block); add_base(ckp, sdata, wb, &new_block);
/* Reset the update time to avoid stacked low priority notifies. Bring /* Reset the update time to avoid stacked low priority notifies. Bring
* forward the next notify in case of a new block. */ * forward the next notify in case of a new block. */
@ -1045,7 +1047,6 @@ retry:
if (new_block) if (new_block)
now_t -= ckp->update_interval / 2; now_t -= ckp->update_interval / 2;
sdata->update_time = now_t; sdata->update_time = now_t;
cksem_post(&sdata->update_sem);
if (new_block) if (new_block)
LOGNOTICE("Block hash changed to %s", sdata->lastswaphash); LOGNOTICE("Block hash changed to %s", sdata->lastswaphash);
@ -1053,6 +1054,8 @@ retry:
ret = true; ret = true;
LOGINFO("Broadcast updated stratum base"); LOGINFO("Broadcast updated stratum base");
out: out:
cksem_post(&sdata->update_sem);
/* Send a ping to miners if we fail to get a base to keep them /* Send a ping to miners if we fail to get a base to keep them
* connected while bitcoind recovers(?) */ * connected while bitcoind recovers(?) */
if (unlikely(!ret)) { if (unlikely(!ret)) {
@ -1206,6 +1209,8 @@ static sdata_t *duplicate_sdata(const sdata_t *sdata)
/* Give the sbuproxy its own workbase list and lock */ /* Give the sbuproxy its own workbase list and lock */
cklock_init(&dsdata->workbase_lock); cklock_init(&dsdata->workbase_lock);
cksem_init(&dsdata->update_sem);
cksem_post(&dsdata->update_sem);
return dsdata; return dsdata;
} }

Loading…
Cancel
Save