Parcourir la source

Make the content code unthreaded and remove the no longer needed inter

thread messaging code. Also simplify some interfaces by polling for
state changes when appropriate instead of being called directly at any
time.
master
Richard Nyberg il y a 18 ans
Parent
révision
95e83bb247
7 fichiers modifiés avec 301 ajouts et 604 suppressions
  1. +6
    -11
      btpd/btpd.c
  2. +0
    -9
      btpd/btpd.h
  3. +292
    -492
      btpd/content.c
  4. +2
    -1
      btpd/content.h
  5. +0
    -87
      btpd/td.c
  6. +1
    -3
      btpd/torrent.h
  7. +0
    -1
      btpd/tracker_req.c

+ 6
- 11
btpd/btpd.c Voir le fichier

@@ -40,8 +40,9 @@ btpd_shutdown(int grace_seconds)
if (tp->state != T_STOPPING) if (tp->state != T_STOPPING)
torrent_stop(tp); torrent_stop(tp);
if (grace_seconds >= 0) { if (grace_seconds >= 0) {
event_once(-1, EV_TIMEOUT, grace_cb, NULL, if (event_once(-1, EV_TIMEOUT, grace_cb, NULL,
(& (struct timeval) { grace_seconds, 0 })); (& (struct timeval) { grace_seconds, 0 })) != 0)
btpd_err("failed to add event (%s).\n", strerror(errno));
} }
} }
} }
@@ -57,13 +58,6 @@ btpd_get_peer_id(void)
return m_peer_id; return m_peer_id;
} }


void
btpd_on_no_torrents(void)
{
if (m_shutdown)
btpd_exit(0);
}

static void static void
signal_cb(int signal, short type, void *arg) signal_cb(int signal, short type, void *arg)
{ {
@@ -77,9 +71,11 @@ heartbeat_cb(int fd, short type, void *arg)
btpd_ev_add(&m_heartbeat, (& (struct timeval) { 1, 0 })); btpd_ev_add(&m_heartbeat, (& (struct timeval) { 1, 0 }));
btpd_seconds++; btpd_seconds++;
net_on_tick(); net_on_tick();
torrent_on_tick_all();
if (m_shutdown && torrent_count() == 0)
btpd_exit(0);
} }


void td_init(void);
void tr_init(void); void tr_init(void);
void ipc_init(void); void ipc_init(void);


@@ -93,7 +89,6 @@ btpd_init(void)
for (int i = sizeof(BTPD_VERSION); i < 20; i++) for (int i = sizeof(BTPD_VERSION); i < 20; i++)
m_peer_id[i] = rand_between(0, 255); m_peer_id[i] = rand_between(0, 255);


td_init();
net_init(); net_init();
ipc_init(); ipc_init();
ul_init(); ul_init();


+ 0
- 9
btpd/btpd.h Voir le fichier

@@ -67,13 +67,4 @@ int btpd_is_stopping(void);


const uint8_t *btpd_get_peer_id(void); const uint8_t *btpd_get_peer_id(void);


void td_acquire_lock(void);
void td_release_lock(void);

#define td_post_begin td_acquire_lock
void td_post(void (*fun)(void *), void *arg);
void td_post_end(void);

void btpd_on_no_torrents(void);

#endif #endif

+ 292
- 492
btpd/content.c Voir le fichier

@@ -3,8 +3,6 @@


#include <fcntl.h> #include <fcntl.h>
#include <math.h> #include <math.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -14,56 +12,13 @@
#include "btpd.h" #include "btpd.h"
#include "stream.h" #include "stream.h"


struct cm_write_data { struct rstat {
uint32_t begin; time_t mtime;
uint8_t *buf; off_t size;
size_t len;
BTPDQ_ENTRY(cm_write_data) entry;
};

BTPDQ_HEAD(cm_write_data_tq, cm_write_data);

enum cm_op_type {
CM_ALLOC,
CM_SAVE,
CM_START,
CM_TEST,
CM_WRITE
};

struct cm_op {
struct torrent *tp;
int error;
int received;
enum cm_op_type type;
union {
struct {
uint32_t piece;
uint32_t pos;
} alloc;
struct {
volatile sig_atomic_t cancel;
} start;
struct {
uint32_t piece;
uint32_t pos;
int ok;
} test;
struct {
uint32_t piece;
uint32_t pos;
struct cm_write_data_tq q;
} write;
} u;

BTPDQ_ENTRY(cm_op) cm_entry;
BTPDQ_ENTRY(cm_op) td_entry;
}; };


BTPDQ_HEAD(cm_op_tq, cm_op);

struct content { struct content {
int active; enum { CM_INACTIVE, CM_STARTING, CM_ACTIVE } state;


uint32_t npieces_got; uint32_t npieces_got;


@@ -73,11 +28,8 @@ struct content {


uint8_t *piece_field; uint8_t *piece_field;
uint8_t *block_field; uint8_t *block_field;
uint8_t *hold_field;
uint8_t *pos_field; uint8_t *pos_field;


struct cm_op_tq todoq;

struct bt_stream *rds; struct bt_stream *rds;
struct bt_stream *wrs; struct bt_stream *wrs;


@@ -86,15 +38,11 @@ struct content {


#define ZEROBUFLEN (1 << 14) #define ZEROBUFLEN (1 << 14)


struct cm_comm {
struct cm_op_tq q;
pthread_mutex_t lock;
pthread_cond_t cond;
};

static struct cm_comm m_long_comm, m_short_comm;
static const uint8_t m_zerobuf[ZEROBUFLEN]; static const uint8_t m_zerobuf[ZEROBUFLEN];


int stat_and_adjust(struct torrent *tp, struct rstat ret[]);
static int save_resume(struct torrent *tp, struct rstat sbs[]);

static int static int
fd_cb_rd(const char *path, int *fd, void *arg) fd_cb_rd(const char *path, int *fd, void *arg)
{ {
@@ -109,53 +57,153 @@ fd_cb_wr(const char *path, int *fd, void *arg)
return vopen(fd, O_RDWR, "%s/%s", tp->tl->dir, path); return vopen(fd, O_RDWR, "%s/%s", tp->tl->dir, path);
} }


static void struct pct_data {
cm_td_post_common(struct cm_comm *comm, struct cm_op *op) off_t off, remain;
struct torrent *tp;
SHA_CTX sha;
BTPDQ_ENTRY(pct_data) entry;
uint32_t piece;
void (*cb)(struct torrent *, uint32_t, int);
};

BTPDQ_HEAD(pct_tq, pct_data);

static struct pct_tq m_pctq = BTPDQ_HEAD_INITIALIZER(m_pctq);
static void cm_write_done(struct torrent *tp);

struct start_test_data {
struct torrent *tp;
struct rstat *rstat;
uint32_t start;
BTPDQ_ENTRY(start_test_data) entry;
};

BTPDQ_HEAD(std_tq, start_test_data);

static struct std_tq m_startq = BTPDQ_HEAD_INITIALIZER(m_startq);

static struct event m_workev;

#define READBUFLEN (1 << 14)

static int
test_hash(struct torrent *tp, uint8_t *hash, uint32_t piece)
{ {
pthread_mutex_lock(&comm->lock); char piece_hash[SHA_DIGEST_LENGTH];
BTPDQ_INSERT_TAIL(&comm->q, op, td_entry); int fd;
pthread_mutex_unlock(&comm->lock); int err;
pthread_cond_signal(&comm->cond); err = vopen(&fd, O_RDONLY, "torrents/%s/torrent", tp->relpath);
if (err != 0)
btpd_err("failed to open 'torrents/%s/torrent' (%s).\n",
tp->relpath, strerror(err));

lseek(fd, tp->pieces_off + piece * SHA_DIGEST_LENGTH, SEEK_SET);
read(fd, piece_hash, SHA_DIGEST_LENGTH);
close(fd);

return bcmp(hash, piece_hash, SHA_DIGEST_LENGTH);
} }


static void void
cm_td_post_long(struct cm_op *op) pct_create(struct torrent *tp, uint32_t piece,
void (*cb)(struct torrent *, uint32_t, int))
{ {
cm_td_post_common(&m_long_comm, op); struct pct_data *p = btpd_calloc(1, sizeof(*p));
p->piece = piece;
p->tp = tp;
p->off = piece * tp->piece_length;
p->remain = torrent_piece_size(tp, piece);
SHA1_Init(&p->sha);
p->cb = cb;
BTPDQ_INSERT_TAIL(&m_pctq, p, entry);
btpd_ev_add(&m_workev, (& (struct timeval) { 0, 0 }));
} }


static void void
cm_td_post_short(struct cm_op *op) pct_kill(struct pct_data *p)
{ {
cm_td_post_common(&m_short_comm, op); BTPDQ_REMOVE(&m_pctq, p, entry);
free(p);
} }


static void void
run_todo(struct content *cm) pct_run(struct pct_data *p)
{ {
struct cm_op *op = BTPDQ_FIRST(&cm->todoq); char buf[READBUFLEN];
size_t unit = (10 << 14);

while (p->remain > 0 && unit > 0) {
size_t wantread = min(p->remain, sizeof(buf));
if (wantread > unit)
wantread = unit;
if ((errno = bts_get(p->tp->cm->rds, p->off, buf, wantread)) != 0)
btpd_err("IO error on '%s' (%s).\n", bts_filename(p->tp->cm->rds),
strerror(errno));
p->remain -= wantread;
unit -= wantread;
p->off += wantread;
SHA1_Update(&p->sha, buf, wantread);
}
if (p->remain == 0) {
uint8_t hash[SHA_DIGEST_LENGTH];
SHA1_Final(hash, &p->sha);
p->cb(p->tp, p->piece, test_hash(p->tp, hash, p->piece) == 0);
pct_kill(p);
}
}


if (op->type == CM_WRITE && BTPDQ_EMPTY(&op->u.write.q)) { void
BTPDQ_REMOVE(&cm->todoq, op, cm_entry); pct_cb(struct torrent *tp, uint32_t piece, int ok)
free(op); {
if (!BTPDQ_EMPTY(&cm->todoq)) struct content *cm = tp->cm;
run_todo(cm); if (ok) {
return; assert(cm->npieces_got < tp->npieces);
cm->npieces_got++;
set_bit(cm->piece_field, piece);
if (net_active(tp))
dl_on_ok_piece(tp->net, piece);
if (cm_full(tp))
cm_write_done(tp);
} else {
cm->ncontent_bytes -= torrent_piece_size(tp, piece);
bzero(cm->block_field + piece * cm->bppbf, cm->bppbf);
if (net_active(tp))
dl_on_bad_piece(tp->net, piece);
} }
}


if (op->type != CM_START) void
cm_td_post_short(op); work_stop(struct torrent *tp)
else {
cm_td_post_long(op); struct content *cm = tp->cm;
struct pct_data *pct, *next;
if (cm->state == CM_STARTING) {
struct start_test_data *std;
BTPDQ_FOREACH(std, &m_startq, entry)
if (std->tp == tp) {
BTPDQ_REMOVE(&m_startq, std, entry);
free(std->rstat);
free(std);
break;
}
}
BTPDQ_FOREACH_MUTABLE(pct, &m_pctq, entry, next)
if (pct->tp == tp)
pct_kill(pct);
} }


static void static int test_hash(struct torrent *tp, uint8_t *hash, uint32_t piece);
add_todo(struct content *cm, struct cm_op *op) void
worker_cb(int fd, short type, void *arg)
{ {
int was_empty = BTPDQ_EMPTY(&cm->todoq); struct pct_data *p = BTPDQ_FIRST(&m_pctq);
BTPDQ_INSERT_TAIL(&cm->todoq, op, cm_entry); if (p == NULL)
if (was_empty) return;
run_todo(cm); pct_run(p);
if (!BTPDQ_EMPTY(&m_pctq))
event_add(&m_workev, (& (struct timeval) { 0, 0 }));
} }


void void
@@ -165,7 +213,6 @@ cm_kill(struct torrent *tp)
bts_close(cm->rds); bts_close(cm->rds);
free(cm->piece_field); free(cm->piece_field);
free(cm->block_field); free(cm->block_field);
free(cm->hold_field);
free(cm->pos_field); free(cm->pos_field);
free(cm); free(cm);
tp->cm = NULL; tp->cm = NULL;
@@ -174,11 +221,9 @@ cm_kill(struct torrent *tp)
void void
cm_save(struct torrent *tp) cm_save(struct torrent *tp)
{ {
struct content *cm = tp->cm; struct rstat sbs[tp->nfiles];
struct cm_op *op = btpd_calloc(1, sizeof(*op)); if (stat_and_adjust(tp, sbs) == 0)
op->tp = tp; save_resume(tp, sbs);
op->type = CM_SAVE;
add_todo(cm, op);
} }


static void static void
@@ -187,7 +232,7 @@ cm_write_done(struct torrent *tp)
struct content *cm = tp->cm; struct content *cm = tp->cm;


if ((errno = bts_close(cm->wrs)) != 0) if ((errno = bts_close(cm->wrs)) != 0)
btpd_err("Error closing write stream for '%s' (%s).\n", btpd_err("error closing write stream for '%s' (%s).\n",
torrent_name(tp), strerror(errno)); torrent_name(tp), strerror(errno));
cm->wrs = NULL; cm->wrs = NULL;
btpd_ev_del(&cm->save_timer); btpd_ev_del(&cm->save_timer);
@@ -198,30 +243,27 @@ void
cm_stop(struct torrent *tp) cm_stop(struct torrent *tp)
{ {
struct content *cm = tp->cm; struct content *cm = tp->cm;
cm->active = 0; if (cm->state == CM_ACTIVE && !cm_full(tp))
struct cm_op *op = BTPDQ_FIRST(&cm->todoq);
if (op != NULL && op->type == CM_START) {
pthread_mutex_lock(&m_long_comm.lock);
if (op->received)
op->u.start.cancel = 1;
else {
BTPDQ_REMOVE(&m_long_comm.q, op, td_entry);
BTPDQ_REMOVE(&cm->todoq, op, cm_entry);
free(op);
}
pthread_mutex_unlock(&m_long_comm.lock);
} else if (!cm_full(tp))
cm_write_done(tp); cm_write_done(tp);


if (BTPDQ_EMPTY(&cm->todoq)) work_stop(tp);
torrent_on_cm_stopped(tp); cm->state = CM_INACTIVE;
} }


int int
cm_active(struct torrent *tp) cm_active(struct torrent *tp)
{ {
struct content *cm = tp->cm; struct content *cm = tp->cm;
return cm->active || !BTPDQ_EMPTY(&cm->todoq); return cm->state != CM_INACTIVE;
}

int
cm_started(struct torrent *tp)
{
struct content *cm = tp->cm;
return cm->state == CM_ACTIVE;
} }


#define SAVE_INTERVAL (& (struct timeval) { 15, 0 }) #define SAVE_INTERVAL (& (struct timeval) { 15, 0 })
@@ -234,95 +276,21 @@ save_timer_cb(int fd, short type, void *arg)
cm_save(tp); cm_save(tp);
} }


static void
cm_td_cb(void *arg)
{
int err;
struct cm_op *op = arg;
struct torrent *tp = op->tp;
struct content *cm = tp->cm;

if (op->error)
btpd_err("IO error for '%s'.\n", torrent_name(tp));

switch (op->type) {
case CM_ALLOC:
set_bit(cm->pos_field, op->u.alloc.pos);
clear_bit(cm->hold_field, op->u.alloc.piece);
break;
case CM_START:
if (cm->active) {
assert(!op->u.start.cancel);
if (!cm_full(tp)) {
if ((err = bts_open(&cm->wrs, tp->nfiles, tp->files,
fd_cb_wr, tp)) != 0)
btpd_err("Couldn't open write stream for '%s' (%s).\n",
torrent_name(tp), strerror(err));
btpd_ev_add(&cm->save_timer, SAVE_INTERVAL);
}
torrent_on_cm_started(tp);
}
break;
case CM_TEST:
if (op->u.test.ok) {
assert(cm->npieces_got < tp->npieces);
cm->npieces_got++;
set_bit(cm->piece_field, op->u.test.piece);
if (net_active(tp))
dl_on_ok_piece(op->tp->net, op->u.test.piece);
if (cm_full(tp))
cm_write_done(tp);
} else {
cm->ncontent_bytes -= torrent_piece_size(tp, op->u.test.piece);
bzero(cm->block_field + op->u.test.piece * cm->bppbf, cm->bppbf);
if (net_active(tp))
dl_on_bad_piece(tp->net, op->u.test.piece);
}
break;
case CM_SAVE:
case CM_WRITE:
break;
}
BTPDQ_REMOVE(&cm->todoq, op, cm_entry);
free(op);
if (!BTPDQ_EMPTY(&cm->todoq))
run_todo(cm);
else if (!cm->active)
torrent_on_cm_stopped(tp);
}

void void
cm_create(struct torrent *tp) cm_create(struct torrent *tp, const char *mi)
{ {
size_t pfield_size = ceil(tp->npieces / 8.0); size_t pfield_size = ceil(tp->npieces / 8.0);
struct content *cm = btpd_calloc(1, sizeof(*cm)); struct content *cm = btpd_calloc(1, sizeof(*cm));
cm->bppbf = ceil((double)tp->piece_length / (1 << 17)); cm->bppbf = ceil((double)tp->piece_length / (1 << 17));
cm->piece_field = btpd_calloc(pfield_size, 1); cm->piece_field = btpd_calloc(pfield_size, 1);
cm->hold_field = btpd_calloc(pfield_size, 1);
cm->pos_field = btpd_calloc(pfield_size, 1); cm->pos_field = btpd_calloc(pfield_size, 1);
cm->block_field = btpd_calloc(tp->npieces * cm->bppbf, 1); cm->block_field = btpd_calloc(tp->npieces * cm->bppbf, 1);


BTPDQ_INIT(&cm->todoq);
evtimer_set(&cm->save_timer, save_timer_cb, tp); evtimer_set(&cm->save_timer, save_timer_cb, tp);


tp->cm = cm; tp->cm = cm;
} }


void
cm_start(struct torrent *tp)
{
struct content *cm = tp->cm;

if ((errno = bts_open(&cm->rds, tp->nfiles, tp->files, fd_cb_rd, tp)) != 0)
btpd_err("Error opening stream (%s).\n", strerror(errno));

cm->active = 1;
struct cm_op *op = btpd_calloc(1, sizeof(*op));
op->tp = tp;
op->type = CM_START;
add_todo(cm, op);
}

int int
cm_get_bytes(struct torrent *tp, uint32_t piece, uint32_t begin, size_t len, cm_get_bytes(struct torrent *tp, uint32_t piece, uint32_t begin, size_t len,
uint8_t **buf) uint8_t **buf)
@@ -331,32 +299,11 @@ cm_get_bytes(struct torrent *tp, uint32_t piece, uint32_t begin, size_t len,
int err = int err =
bts_get(tp->cm->rds, piece * tp->piece_length + begin, *buf, len); bts_get(tp->cm->rds, piece * tp->piece_length + begin, *buf, len);
if (err != 0) if (err != 0)
btpd_err("Io error (%s)\n", strerror(err)); btpd_err("IO error on '%s' (%s).\n", bts_filename(tp->cm->rds),
strerror(err));
return 0; return 0;
} }


static void
cm_post_alloc(struct torrent *tp, uint32_t piece)
{
struct content *cm = tp->cm;
set_bit(cm->hold_field, piece);

struct cm_op *op = btpd_calloc(1, sizeof(*op));
op->tp = tp;
op->type = CM_ALLOC;
op->u.alloc.piece = piece;
op->u.alloc.pos = piece;
add_todo(cm, op);

op = btpd_calloc(1, sizeof(*op));
op->tp = tp;
op->type = CM_WRITE;
op->u.write.piece = piece;
op->u.write.pos = piece;
BTPDQ_INIT(&op->u.write.q);
add_todo(cm, op);
}

void void
cm_prealloc(struct torrent *tp, uint32_t piece) cm_prealloc(struct torrent *tp, uint32_t piece)
{ {
@@ -364,30 +311,12 @@ cm_prealloc(struct torrent *tp, uint32_t piece)


if (cm_alloc_size <= 0) if (cm_alloc_size <= 0)
set_bit(cm->pos_field, piece); set_bit(cm->pos_field, piece);
else {
unsigned npieces = ceil((double)cm_alloc_size / tp->piece_length);
uint32_t start = piece - piece % npieces;
uint32_t end = min(start + npieces, tp->npieces);

while (start < end) {
if ((!has_bit(cm->pos_field, start)
&& !has_bit(cm->hold_field, start)))
cm_post_alloc(tp, start);
start++;
}
}
} }


void void
cm_test_piece(struct torrent *tp, uint32_t piece) cm_test_piece(struct torrent *tp, uint32_t piece)
{ {
struct content *cm = tp->cm; pct_create(tp, piece, pct_cb);
struct cm_op *op = btpd_calloc(1, sizeof(*op));
op->tp = tp;
op->type = CM_TEST;
op->u.test.piece = piece;
op->u.test.pos = piece;
add_todo(cm, op);
} }


int int
@@ -397,30 +326,33 @@ cm_put_bytes(struct torrent *tp, uint32_t piece, uint32_t begin,
int err; int err;
struct content *cm = tp->cm; struct content *cm = tp->cm;


if (has_bit(cm->hold_field, piece)) { if (!has_bit(cm->pos_field, piece)) {
struct cm_write_data *d = btpd_calloc(1, sizeof(*d) + len); unsigned npieces = ceil((double)cm_alloc_size / tp->piece_length);
d->begin = begin; uint32_t start = piece - piece % npieces;
d->len = len; uint32_t end = min(start + npieces, tp->npieces);
d->buf = (uint8_t *)(d + 1); while (start < end) {
bcopy(buf, d->buf, len); if (!has_bit(cm->pos_field, start)) {
struct cm_op *op; off_t len = torrent_piece_size(tp, start);
BTPDQ_FOREACH(op, &cm->todoq, cm_entry) off_t off = tp->piece_length * start;
if (op->type == CM_WRITE && op->u.write.piece == piece) while (len > 0) {
break; size_t wlen = min(ZEROBUFLEN, len);
struct cm_write_data *it; if ((err = bts_put(cm->wrs, off, m_zerobuf, wlen)) != 0)
BTPDQ_FOREACH(it, &op->u.write.q, entry) btpd_err("IO error on '%s' (%s).\n",
if (it->begin > begin) { bts_filename(cm->wrs), strerror(errno));
BTPDQ_INSERT_BEFORE(it, d, entry); len -= wlen;
break; off += wlen;
}
set_bit(cm->pos_field, start);
} }
if (it == NULL) start++;
BTPDQ_INSERT_TAIL(&op->u.write.q, d, entry); }
} else {
err = bts_put(cm->wrs, piece * tp->piece_length + begin, buf,
len);
if (err != 0)
btpd_err("Io error (%s)\n", strerror(err));
} }
err = bts_put(cm->wrs, piece * tp->piece_length + begin, buf, len);
if (err != 0)
btpd_err("IO error on '%s' (%s)\n", bts_filename(cm->wrs),
strerror(err));


cm->ncontent_bytes += len; cm->ncontent_bytes += len;
uint8_t *bf = cm->block_field + piece * cm->bppbf; uint8_t *bf = cm->block_field + piece * cm->bppbf;
@@ -465,114 +397,6 @@ cm_has_piece(struct torrent *tp, uint32_t piece)
return has_bit(tp->cm->piece_field, piece); return has_bit(tp->cm->piece_field, piece);
} }


static int
test_hash(struct torrent *tp, uint8_t *hash, uint32_t piece)
{
char piece_hash[SHA_DIGEST_LENGTH];
int fd;
int err;

err = vopen(&fd, O_RDONLY, "torrents/%s/torrent", tp->relpath);
if (err != 0)
btpd_err("test_hash: %s\n", strerror(err));

lseek(fd, tp->pieces_off + piece * SHA_DIGEST_LENGTH, SEEK_SET);
read(fd, piece_hash, SHA_DIGEST_LENGTH);
close(fd);

return bcmp(hash, piece_hash, SHA_DIGEST_LENGTH);
}

static int
test_piece(struct torrent *tp, uint32_t pos, uint32_t piece, int *ok)
{
int err;
uint8_t hash[SHA_DIGEST_LENGTH];
struct bt_stream *bts;
if ((err = bts_open(&bts, tp->nfiles, tp->files, fd_cb_rd, tp)) != 0)
return err;
if ((err = bts_sha(bts, pos * tp->piece_length,
torrent_piece_size(tp, piece), hash)) != 0)
return err;;
bts_close(bts);
*ok = test_hash(tp, hash, piece) == 0;
return 0;
}

static void
cm_td_alloc(struct cm_op *op)
{
struct torrent *tp = op->tp;
struct content *cm = tp->cm;
uint32_t pos = op->u.alloc.pos;
struct bt_stream *bts;
int err;

assert(!has_bit(cm->pos_field, pos));

if ((err = bts_open(&bts, tp->nfiles, tp->files, fd_cb_wr, tp)) != 0)
goto out;

off_t len = torrent_piece_size(tp, pos);
off_t off = tp->piece_length * pos;
while (len > 0) {
size_t wlen = min(ZEROBUFLEN, len);
if ((err = bts_put(bts, off, m_zerobuf, wlen)) != 0) {
bts_close(bts);
goto out;
}
len -= wlen;
off += wlen;
}
err = bts_close(bts);
out:
if (err != 0)
op->error = 1;
}

static int
test_torrent(struct torrent *tp, volatile sig_atomic_t *cancel)
{
int err;
FILE *fp;
uint8_t (*hashes)[SHA_DIGEST_LENGTH];
uint8_t hash[SHA_DIGEST_LENGTH];

if ((err = vfopen(&fp, "r", "torrents/%s/torrent", tp->relpath)) != 0)
return err;

hashes = btpd_malloc(tp->npieces * SHA_DIGEST_LENGTH);
fseek(fp, tp->pieces_off, SEEK_SET);
fread(hashes, SHA_DIGEST_LENGTH, tp->npieces, fp);
fclose(fp);

struct content *cm = tp->cm;
for (uint32_t piece = 0; piece < tp->npieces; piece++) {
if (!has_bit(cm->pos_field, piece))
continue;
err = bts_sha(cm->rds, piece * tp->piece_length,
torrent_piece_size(tp, piece), hash);
if (err != 0)
break;
if (bcmp(hashes[piece], hash, SHA_DIGEST_LENGTH) == 0)
set_bit(tp->cm->piece_field, piece);
else
clear_bit(tp->cm->piece_field, piece);
if (*cancel) {
err = EINTR;
break;
}
}

free(hashes);
return err;
}

struct rstat {
time_t mtime;
off_t size;
};

int int
stat_and_adjust(struct torrent *tp, struct rstat ret[]) stat_and_adjust(struct torrent *tp, struct rstat ret[])
{ {
@@ -655,52 +479,13 @@ save_resume(struct torrent *tp, struct rstat sbs[])
return err; return err;
} }


static void void start_test_cb(struct torrent *tp, uint32_t piece, int ok);
cm_td_save(struct cm_op *op)
{
struct torrent *tp = op->tp;
struct rstat sbs[tp->nfiles];
if (stat_and_adjust(tp, sbs) == 0)
save_resume(tp, sbs);
}


static void void
cm_td_start(struct cm_op *op) start_test_end(struct torrent *tp, int unclean)
{ {
int err, resume_clean = 0, tested_torrent = 0;
struct rstat sbs[op->tp->nfiles];
struct torrent *tp = op->tp;
struct content *cm = tp->cm; struct content *cm = tp->cm;


if ((err = stat_and_adjust(op->tp, sbs)) != 0)
goto out;

resume_clean = load_resume(tp, sbs) == 0;
if (!resume_clean) {
memset(cm->pos_field, 0xff, ceil(tp->npieces / 8.0));
off_t off = 0;
for (int i = 0; i < tp->nfiles; i++) {
if (sbs[i].size != tp->files[i].length) {
uint32_t start, end;
end = (off + tp->files[i].length - 1)
/ tp->piece_length;
start = (off + sbs[i].size) / tp->piece_length;
while (start <= end) {
clear_bit(cm->pos_field, start);
clear_bit(cm->piece_field, start);
bzero(cm->block_field + start * cm->bppbf, cm->bppbf);
start++;
}
}
off += tp->files[i].length;
}
if (op->u.start.cancel)
goto out;
if ((err = test_torrent(tp, &op->u.start.cancel)) != 0)
goto out;
tested_torrent = 1;
}

bzero(cm->pos_field, ceil(tp->npieces / 8.0)); bzero(cm->pos_field, ceil(tp->npieces / 8.0));
for (uint32_t piece = 0; piece < tp->npieces; piece++) { for (uint32_t piece = 0; piece < tp->npieces; piece++) {
if (cm_has_piece(tp, piece)) { if (cm_has_piece(tp, piece)) {
@@ -720,113 +505,128 @@ cm_td_start(struct cm_op *op)
} }
} }
if (nblocks_got == nblocks) { if (nblocks_got == nblocks) {
resume_clean = 0; bzero(bf, cm->bppbf);
int ok = 0; cm->ncontent_bytes -= torrent_piece_size(tp, piece);
if (!tested_torrent) {
if (((err = test_piece(tp, piece, piece, &ok)) != 0
|| op->u.start.cancel))
goto out;
}
if (ok) {
set_bit(cm->pos_field, piece);
set_bit(cm->piece_field, piece);
} else {
bzero(bf, cm->bppbf);
cm->ncontent_bytes -= torrent_piece_size(tp, piece);
}
} else if (nblocks_got > 0) } else if (nblocks_got > 0)
set_bit(cm->pos_field, piece); set_bit(cm->pos_field, piece);
} }
if (!cm_full(tp)) {
int err;
if ((err = bts_open(&cm->wrs, tp->nfiles, tp->files,
fd_cb_wr, tp)) != 0)
btpd_err("failed to open write stream for '%s' (%s).\n",
torrent_name(tp), strerror(err));
btpd_ev_add(&cm->save_timer, SAVE_INTERVAL);
}
if (unclean) {
struct start_test_data *std = BTPDQ_FIRST(&m_startq);


if (!resume_clean) assert(std->tp == tp);
save_resume(tp, sbs); BTPDQ_REMOVE(&m_startq, std, entry);
save_resume(tp, std->rstat);
free(std->rstat);
free(std);


out: if ((std = BTPDQ_FIRST(&m_startq)) != NULL)
if (!op->u.start.cancel && err != 0) pct_create(std->tp, std->start, start_test_cb);
op->error = 1; }
cm->state = CM_ACTIVE;
} }


static void void
cm_td_test(struct cm_op *op) start_test_cb(struct torrent *tp, uint32_t piece, int ok)
{ {
if (test_piece(op->tp, op->u.test.pos, op->u.test.piece, struct content *cm = tp->cm;
&op->u.test.ok) != 0) if (ok)
op->error = 1; set_bit(cm->piece_field, piece);
else
clear_bit(cm->piece_field, piece);
piece++;
while (piece < tp->npieces && !has_bit(cm->pos_field, piece))
piece++;
if (piece < tp->npieces)
pct_create(tp, piece, start_test_cb);
else
start_test_end(tp, 1);
} }


static void void
cm_td_write(struct cm_op *op) start_test(struct torrent *tp, struct rstat *sbs)
{ {
int err; uint32_t piece = 0;
struct cm_write_data *d, *next; struct content *cm = tp->cm;
off_t base = op->tp->piece_length * op->u.write.pos; while (piece < tp->npieces && !has_bit(cm->pos_field, piece))
struct bt_stream *bts; piece++;
if ((err = bts_open(&bts, op->tp->nfiles, op->tp->files, if (piece < tp->npieces) {
fd_cb_wr, op->tp)) != 0) struct start_test_data *std = btpd_calloc(1, sizeof(*std));
goto out; std->tp = tp;
BTPDQ_FOREACH(d, &op->u.write.q, entry) std->start = piece;
if ((err = bts_put(bts, base + d->begin, d->buf, d->len)) != 0) { std->rstat = sbs;
bts_close(bts); BTPDQ_INSERT_TAIL(&m_startq, std, entry);
goto out; if (std == BTPDQ_FIRST(&m_startq))
} pct_create(tp, piece, start_test_cb);
err = bts_close(bts); } else {
out: free(sbs);
BTPDQ_FOREACH_MUTABLE(d, &op->u.write.q, entry, next) start_test_end(tp, 0);
free(d); }
if (err)
op->error = 1;
} }


static void void
cm_td(void *arg) cm_start(struct torrent *tp)
{ {
struct cm_comm *comm = arg; int err, resume_clean = 0;
struct cm_op *op; struct rstat *sbs;
for (;;) { struct content *cm = tp->cm;
pthread_mutex_lock(&comm->lock); if ((errno = bts_open(&cm->rds, tp->nfiles, tp->files, fd_cb_rd, tp)) != 0)
while (BTPDQ_EMPTY(&comm->q)) btpd_err("failed to open stream for '%s' (%s).\n",
pthread_cond_wait(&comm->cond, &comm->lock); torrent_name(tp), strerror(errno));

cm->state = CM_STARTING;
op = BTPDQ_FIRST(&comm->q); sbs = btpd_calloc(tp->nfiles, sizeof(*sbs));
BTPDQ_REMOVE(&comm->q, op, td_entry); if ((err = stat_and_adjust(tp, sbs)) != 0)
op->received = 1; btpd_err("failed stat_and_adjust for '%s' (%s).\n",
pthread_mutex_unlock(&comm->lock); torrent_name(tp), strerror(err));

resume_clean = load_resume(tp, sbs) == 0;
switch (op->type) { if (!resume_clean) {
case CM_ALLOC: memset(cm->pos_field, 0xff, ceil(tp->npieces / 8.0));
cm_td_alloc(op); off_t off = 0;
break; for (int i = 0; i < tp->nfiles; i++) {
case CM_SAVE: if (sbs[i].size != tp->files[i].length) {
cm_td_save(op); uint32_t start, end;
break; end = (off + tp->files[i].length - 1)
case CM_START: / tp->piece_length;
cm_td_start(op); start = (off + sbs[i].size) / tp->piece_length;
break; while (start <= end) {
case CM_TEST: clear_bit(cm->pos_field, start);
cm_td_test(op); clear_bit(cm->piece_field, start);
break; bzero(cm->block_field + start * cm->bppbf, cm->bppbf);
case CM_WRITE: start++;
cm_td_write(op); }
break; }
default: off += tp->files[i].length;
abort();
} }
td_post_begin();
td_post(cm_td_cb, op);
td_post_end();
} }
for (uint32_t piece = 0; piece < tp->npieces; piece++) {
if (has_bit(cm->piece_field, piece))
continue;
uint8_t *bf = cm->block_field + cm->bppbf * piece;
uint32_t nblocks = torrent_piece_blocks(tp, piece);
uint32_t block = 0;
while (block < nblocks && has_bit(bf, block))
block++;
if (block == nblocks)
set_bit(cm->pos_field, piece);
}

start_test(tp, sbs);
} }


void void
cm_init(void) cm_init(void)
{ {
pthread_t td; evtimer_set(&m_workev, worker_cb, NULL);
BTPDQ_INIT(&m_long_comm.q);
pthread_mutex_init(&m_long_comm.lock, NULL);
pthread_cond_init(&m_long_comm.cond, NULL);
pthread_create(&td, NULL, (void *(*)(void *))cm_td, &m_long_comm);
BTPDQ_INIT(&m_short_comm.q);
pthread_mutex_init(&m_short_comm.lock, NULL);
pthread_cond_init(&m_short_comm.cond, NULL);
pthread_create(&td, NULL, (void *(*)(void *))cm_td, &m_short_comm);
} }

+ 2
- 1
btpd/content.h Voir le fichier

@@ -3,13 +3,14 @@


void cm_init(void); void cm_init(void);


void cm_create(struct torrent *tp); void cm_create(struct torrent *tp, const char *mi);
void cm_kill(struct torrent *tp); void cm_kill(struct torrent *tp);


void cm_start(struct torrent *tp); void cm_start(struct torrent *tp);
void cm_stop(struct torrent * tp); void cm_stop(struct torrent * tp);


int cm_active(struct torrent *tp); int cm_active(struct torrent *tp);
int cm_started(struct torrent *tp);
int cm_full(struct torrent *tp); int cm_full(struct torrent *tp);


off_t cm_content(struct torrent *tp); off_t cm_content(struct torrent *tp);


+ 0
- 87
btpd/td.c Voir le fichier

@@ -1,87 +0,0 @@
#include <sys/types.h>

#include <pthread.h>
#include <string.h>
#include <unistd.h>

#include "btpd.h"

struct td_cb {
void (*cb)(void *);
void *arg;
BTPDQ_ENTRY(td_cb) entry;
};

BTPDQ_HEAD(td_cb_tq, td_cb);

static int m_td_rd, m_td_wr;
static struct event m_td_ev;
static struct td_cb_tq m_td_cbs = BTPDQ_HEAD_INITIALIZER(m_td_cbs);
static pthread_mutex_t m_td_lock;

void
td_acquire_lock(void)
{
pthread_mutex_lock(&m_td_lock);
}

void
td_release_lock(void)
{
pthread_mutex_unlock(&m_td_lock);
}

void
td_post(void (*fun)(void *), void *arg)
{
struct td_cb *cb = btpd_calloc(1, sizeof(*cb));
cb->cb = fun;
cb->arg = arg;
BTPDQ_INSERT_TAIL(&m_td_cbs, cb, entry);
}

void
td_post_end(void)
{
char c = '1';
td_release_lock();
write(m_td_wr, &c, sizeof(c));
}

static void
td_cb(int fd, short type, void *arg)
{
char buf[1024];
struct td_cb_tq tmpq = BTPDQ_HEAD_INITIALIZER(tmpq);
struct td_cb *cb, *next;

read(fd, buf, sizeof(buf));
td_acquire_lock();
BTPDQ_FOREACH_MUTABLE(cb, &m_td_cbs, entry, next)
BTPDQ_INSERT_TAIL(&tmpq, cb, entry);
BTPDQ_INIT(&m_td_cbs);
td_release_lock();

BTPDQ_FOREACH_MUTABLE(cb, &tmpq, entry, next) {
cb->cb(cb->arg);
free(cb);
}
}

void
td_init(void)
{
int err;
int fds[2];
if (pipe(fds) == -1) {
btpd_err("Couldn't create thread callback pipe (%s).\n",
strerror(errno));
}
m_td_rd = fds[0];
m_td_wr = fds[1];
if ((err = pthread_mutex_init(&m_td_lock, NULL)) != 0)
btpd_err("Couldn't create mutex (%s).\n", strerror(err));

event_set(&m_td_ev, m_td_rd, EV_READ|EV_PERSIST, td_cb, NULL);
btpd_ev_add(&m_td_ev, NULL);
}

+ 1
- 3
btpd/torrent.h Voir le fichier

@@ -47,8 +47,6 @@ uint32_t torrent_block_size(struct torrent *tp, uint32_t piece,
uint32_t nblocks, uint32_t block); uint32_t nblocks, uint32_t block);
const char *torrent_name(struct torrent *tp); const char *torrent_name(struct torrent *tp);


void torrent_on_cm_stopped(struct torrent *tp); void torrent_on_tick_all(void);
void torrent_on_cm_started(struct torrent *tp);
void torrent_on_tr_stopped(struct torrent *tp);


#endif #endif

+ 0
- 1
btpd/tracker_req.c Voir le fichier

@@ -133,7 +133,6 @@ tr_set_stopped(struct torrent *tp)
tr->ttype = TIMER_NONE; tr->ttype = TIMER_NONE;
if (tr->req != NULL) if (tr->req != NULL)
tr_cancel(tr); tr_cancel(tr);
torrent_on_tr_stopped(tp);
} }


void void


||||||
x
 
000:0
Chargement…
Annuler
Enregistrer