diff options
Diffstat (limited to 'toxcore/Messenger.c')
-rw-r--r-- | toxcore/Messenger.c | 158 |
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 | ||
1502 | static 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 | */ | ||
1519 | static 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 | ||
1595 | static 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 | */ |
1592 | static void break_files(const Messenger *m, int32_t friendnumber) | 1634 | static 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 | } |