summaryrefslogtreecommitdiff
path: root/toxcore/Messenger.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxcore/Messenger.c')
-rw-r--r--toxcore/Messenger.c158
1 files changed, 99 insertions, 59 deletions
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c
index 3baf7401..a6bccfdc 100644
--- a/toxcore/Messenger.c
+++ b/toxcore/Messenger.c
@@ -1482,119 +1482,159 @@ uint64_t file_dataremaining(const Messenger *m, int32_t friendnumber, uint8_t fi
1482 return 0; 1482 return 0;
1483 } 1483 }
1484 1484
1485 const struct File_Transfers *const sending = &m->friendlist[friendnumber].file_sending[filenumber];
1486
1485 if (send_receive == 0) { 1487 if (send_receive == 0) {
1486 if (m->friendlist[friendnumber].file_sending[filenumber].status == FILESTATUS_NONE) { 1488 if (sending->status == FILESTATUS_NONE) {
1487 return 0; 1489 return 0;
1488 } 1490 }
1489 1491
1490 return m->friendlist[friendnumber].file_sending[filenumber].size - 1492 return sending->size - sending->transferred;
1491 m->friendlist[friendnumber].file_sending[filenumber].transferred;
1492 } 1493 }
1493 1494
1494 if (m->friendlist[friendnumber].file_receiving[filenumber].status == FILESTATUS_NONE) { 1495 const struct File_Transfers *const receiving = &m->friendlist[friendnumber].file_receiving[filenumber];
1496
1497 if (receiving->status == FILESTATUS_NONE) {
1495 return 0; 1498 return 0;
1496 } 1499 }
1497 1500
1498 return m->friendlist[friendnumber].file_receiving[filenumber].size - 1501 return receiving->size - receiving->transferred;
1499 m->friendlist[friendnumber].file_receiving[filenumber].transferred;
1500} 1502}
1501 1503
1502static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber, void *userdata) 1504/**
1505 * Iterate over all file transfers and request chunks (from the client) for each
1506 * of them.
1507 *
1508 * The free_slots parameter is updated by this function.
1509 *
1510 * @param m Our messenger object.
1511 * @param friendnumber The friend we're sending files to.
1512 * @param userdata The client userdata to pass along to chunk request callbacks.
1513 * @param free_slots A pointer to the number of free send queue slots in the
1514 * crypto connection.
1515 *
1516 * @return true if there are still file transfers ongoing, false if all file
1517 * transfers are complete.
1518 */
1519static bool do_all_filetransfers(Messenger *m, int32_t friendnumber, void *userdata, uint32_t *free_slots)
1503{ 1520{
1504 if (!m->friendlist[friendnumber].num_sending_files) { 1521 Friend *const friendcon = &m->friendlist[friendnumber];
1505 return; 1522 uint32_t num = friendcon->num_sending_files;
1506 }
1507 1523
1508 int free_slots = crypto_num_free_sendqueue_slots(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, 1524 bool any_active_fts = false;
1509 m->friendlist[friendnumber].friendcon_id));
1510
1511 if (free_slots < MIN_SLOTS_FREE) {
1512 free_slots = 0;
1513 } else {
1514 free_slots -= MIN_SLOTS_FREE;
1515 }
1516 1525
1517 unsigned int i, num = m->friendlist[friendnumber].num_sending_files; 1526 // Iterate over all file transfers, including inactive ones. I.e. we always
1518 1527 // iterate exactly MAX_CONCURRENT_FILE_PIPES times.
1519 for (i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) { 1528 for (uint32_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
1520 struct File_Transfers *ft = &m->friendlist[friendnumber].file_sending[i]; 1529 struct File_Transfers *const ft = &friendcon->file_sending[i];
1521 1530
1531 // Any status other than NONE means the file transfer is active.
1522 if (ft->status != FILESTATUS_NONE) { 1532 if (ft->status != FILESTATUS_NONE) {
1533 any_active_fts = true;
1523 --num; 1534 --num;
1524 1535
1525 if (ft->status == FILESTATUS_FINISHED) { 1536 // If the file transfer is complete, we request a chunk of size 0.
1526 /* Check if file was entirely sent. */ 1537 if (ft->status == FILESTATUS_FINISHED && friend_received_packet(m, friendnumber, ft->last_packet_number) == 0) {
1527 if (friend_received_packet(m, friendnumber, ft->last_packet_number) == 0) { 1538 if (m->file_reqchunk) {
1528 if (m->file_reqchunk) { 1539 m->file_reqchunk(m, friendnumber, i, ft->transferred, 0, userdata);
1529 (*m->file_reqchunk)(m, friendnumber, i, ft->transferred, 0, userdata);
1530 }
1531
1532 ft->status = FILESTATUS_NONE;
1533 --m->friendlist[friendnumber].num_sending_files;
1534 } 1540 }
1535 }
1536 1541
1537 /* TODO(irungentoo): if file is too slow, switch to the next. */ 1542 // Now it's inactive, we're no longer sending this.
1538 if (ft->slots_allocated > (unsigned int)free_slots) { 1543 ft->status = FILESTATUS_NONE;
1539 free_slots = 0; 1544 --friendcon->num_sending_files;
1540 } else {
1541 free_slots -= ft->slots_allocated;
1542 } 1545 }
1546
1547 // Decrease free slots by the number of slots this FT uses.
1548 *free_slots = max_s32(0, (int32_t) * free_slots - ft->slots_allocated);
1543 } 1549 }
1544 1550
1545 while (ft->status == FILESTATUS_TRANSFERRING && (ft->paused == FILE_PAUSE_NOT)) { 1551 if (ft->status == FILESTATUS_TRANSFERRING && ft->paused == FILE_PAUSE_NOT) {
1546 if (max_speed_reached(m->net_crypto, friend_connection_crypt_connection_id(m->fr_c, 1552 if (max_speed_reached(m->net_crypto, friend_connection_crypt_connection_id(
1547 m->friendlist[friendnumber].friendcon_id))) { 1553 m->fr_c, friendcon->friendcon_id))) {
1548 free_slots = 0; 1554 *free_slots = 0;
1549 } 1555 }
1550 1556
1551 if (free_slots == 0) { 1557 if (*free_slots == 0) {
1552 break; 1558 continue;
1553 } 1559 }
1554 1560
1555 uint16_t length = MAX_FILE_DATA_SIZE;
1556
1557 if (ft->size == 0) { 1561 if (ft->size == 0) {
1558 /* Send 0 data to friend if file is 0 length. */ 1562 /* Send 0 data to friend if file is 0 length. */
1559 file_data(m, friendnumber, i, 0, nullptr, 0); 1563 file_data(m, friendnumber, i, 0, nullptr, 0);
1560 break; 1564 continue;
1561 } 1565 }
1562 1566
1563 if (ft->size == ft->requested) { 1567 if (ft->size == ft->requested) {
1564 break; 1568 // This file transfer is done.
1565 } 1569 continue;
1566
1567 if (ft->size - ft->requested < length) {
1568 length = ft->size - ft->requested;
1569 } 1570 }
1570 1571
1571 ++ft->slots_allocated; 1572 // Allocate 1 slot to this file transfer.
1573 ft->slots_allocated++;
1572 1574
1573 uint64_t position = ft->requested; 1575 const uint16_t length = min_u64(ft->size - ft->requested, MAX_FILE_DATA_SIZE);
1576 const uint64_t position = ft->requested;
1574 ft->requested += length; 1577 ft->requested += length;
1575 1578
1576 if (m->file_reqchunk) { 1579 if (m->file_reqchunk) {
1577 (*m->file_reqchunk)(m, friendnumber, i, position, length, userdata); 1580 m->file_reqchunk(m, friendnumber, i, position, length, userdata);
1578 } 1581 }
1579 1582
1580 --free_slots; 1583 // The allocated slot is no longer free.
1584 --*free_slots;
1581 } 1585 }
1582 1586
1583 if (num == 0) { 1587 if (num == 0) {
1584 break; 1588 continue;
1585 } 1589 }
1586 } 1590 }
1591
1592 return any_active_fts;
1587} 1593}
1588 1594
1595static void do_reqchunk_filecb(Messenger *m, int32_t friendnumber, void *userdata)
1596{
1597 // We're not currently doing any file transfers.
1598 if (m->friendlist[friendnumber].num_sending_files == 0) {
1599 return;
1600 }
1601
1602 // The number of packet slots left in the sendbuffer.
1603 // This is a per friend count (CRYPTO_PACKET_BUFFER_SIZE).
1604 uint32_t free_slots = crypto_num_free_sendqueue_slots(
1605 m->net_crypto,
1606 friend_connection_crypt_connection_id(
1607 m->fr_c,
1608 m->friendlist[friendnumber].friendcon_id));
1609
1610 // We keep MIN_SLOTS_FREE slots free for other packets, otherwise file
1611 // transfers might block other traffic for a long time.
1612 free_slots = max_s32(0, (int32_t)free_slots - MIN_SLOTS_FREE);
1613
1614 bool any_active_fts = true;
1615 uint32_t loop_counter = 0;
1616 // Maximum number of outer loops below. If the client doesn't send file
1617 // chunks from within the chunk request callback handler, we never realise
1618 // that the file transfer has finished and may end up in an infinite loop.
1619 //
1620 // TODO(zoff99): Fix this to exit the loop properly when we're done
1621 // requesting all chunks for all file transfers.
1622 const uint32_t MAX_FT_LOOPS = 4;
1623
1624 while (((free_slots > 0) || loop_counter == 0) && any_active_fts && (loop_counter < MAX_FT_LOOPS)) {
1625 any_active_fts = do_all_filetransfers(m, friendnumber, userdata, &free_slots);
1626 loop_counter++;
1627 }
1628}
1629
1630
1589/* Run this when the friend disconnects. 1631/* Run this when the friend disconnects.
1590 * Kill all current file transfers. 1632 * Kill all current file transfers.
1591 */ 1633 */
1592static void break_files(const Messenger *m, int32_t friendnumber) 1634static void break_files(const Messenger *m, int32_t friendnumber)
1593{ 1635{
1594 uint32_t i;
1595
1596 // TODO(irungentoo): Inform the client which file transfers get killed with a callback? 1636 // TODO(irungentoo): Inform the client which file transfers get killed with a callback?
1597 for (i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) { 1637 for (uint32_t i = 0; i < MAX_CONCURRENT_FILE_PIPES; ++i) {
1598 if (m->friendlist[friendnumber].file_sending[i].status != FILESTATUS_NONE) { 1638 if (m->friendlist[friendnumber].file_sending[i].status != FILESTATUS_NONE) {
1599 m->friendlist[friendnumber].file_sending[i].status = FILESTATUS_NONE; 1639 m->friendlist[friendnumber].file_sending[i].status = FILESTATUS_NONE;
1600 } 1640 }