Changeset 9c16c36 in mainline for uspace/lib/c/generic/async/client.c


Ignore:
Timestamp:
2018-06-07T17:00:32Z (6 years ago)
Author:
Jiří Zárevúcky <jiri.zarevucky@…>
Children:
95faa4d
Parents:
8404342
git-author:
Jiří Zárevúcky <jiri.zarevucky@…> (2018-05-25 17:07:26)
git-committer:
Jiří Zárevúcky <jiri.zarevucky@…> (2018-06-07 17:00:32)
Message:

initial implementation of a new async call api

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/async/client.c

    r8404342 r9c16c36  
    13461346}
    13471347
     1348void async_call_begin(async_call_t *call, async_sess_t *sess, sysarg_t imethod,
     1349    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
     1350{
     1351        memset(call, 0, sizeof(*call));
     1352
     1353        call->exch = async_exchange_begin(sess);
     1354        if (!call->exch) {
     1355                call->rc = ENOMEM;
     1356                return;
     1357        }
     1358
     1359        async_call_method(call, &call->initial, imethod,
     1360            arg1, arg2, arg3, arg4);
     1361}
     1362
     1363static suseconds_t time_until(const struct timeval *t)
     1364{
     1365        struct timeval tv;
     1366        getuptime(&tv);
     1367        if (tv_gteq(&tv, t))
     1368                return 1;
     1369
     1370        return tv_sub_diff(t, &tv);
     1371}
     1372
     1373static errno_t async_call_finish_internal(async_call_t *call, const struct timeval *expires)
     1374{
     1375        errno_t rc;
     1376
     1377        /* Wait for all the fragments. */
     1378        while (!list_empty(&call->fragments)) {
     1379                link_t *tmp = list_first(&call->fragments);
     1380                async_call_data_t *frag =
     1381                    list_get_instance(tmp, async_call_data_t, link);
     1382
     1383                if (expires) {
     1384                        errno_t trc = async_wait_timeout(frag->msgid, &rc,
     1385                            time_until(expires));
     1386                        if (trc != EOK)
     1387                                return trc;
     1388                } else {
     1389                        async_wait_for(frag->msgid, &rc);
     1390                }
     1391
     1392                list_remove(tmp);
     1393
     1394                if (rc != EOK)
     1395                        return rc;
     1396
     1397                if (frag->finalizer) {
     1398                        rc = frag->finalizer(frag);
     1399                        if (rc != EOK)
     1400                                return rc;
     1401                }
     1402        }
     1403
     1404        return EOK;
     1405}
     1406
     1407errno_t async_call_finish_timeout(async_call_t *call, const struct timeval *expires)
     1408{
     1409        assert(call);
     1410
     1411        if (call->rc)
     1412                return call->rc;
     1413
     1414        if (call->exch) {
     1415                async_exchange_end(call->exch);
     1416                call->exch = NULL;
     1417        }
     1418
     1419        errno_t rc = async_call_finish_internal(call, expires);
     1420        if (rc == ETIMEOUT)
     1421                return rc;
     1422
     1423        /* If one fails, abort the call. */
     1424        if (rc != EOK)
     1425                async_call_abort(call);
     1426
     1427        assert(list_empty(&call->fragments));
     1428        call->rc = rc;
     1429        return rc;
     1430}
     1431
     1432// Ends the call, and waits for all in-flight fragments to finish.
     1433errno_t async_call_finish(async_call_t *call)
     1434{
     1435        return async_call_finish_timeout(call, NULL);
     1436}
     1437
     1438// Aborts the call. After this function returns, auxiliary structures
     1439// and buffers are safe to deallocate.
     1440extern void async_call_abort(async_call_t *call)
     1441{
     1442        // FIXME: Proper abort needs kernel support. A system call should
     1443        //        clean up bookkeeping structures in the kernel and notify
     1444        //        the server of the abort as well.
     1445
     1446        //        Currently, we just wait, which is less than ideal,
     1447        //        but at the same time, nothing in HelenOS currently
     1448        //        benefits from timeouts.
     1449
     1450        assert(call);
     1451
     1452        if (call->exch) {
     1453                async_exchange_end(call->exch);
     1454                call->exch = NULL;
     1455        }
     1456
     1457        /* Wait for all the fragments. */
     1458        while (!list_empty(&call->fragments)) {
     1459                // TODO: abort instead of waiting
     1460                (void) async_call_finish_internal(call, NULL);
     1461        }
     1462
     1463        assert(list_empty(&call->fragments));
     1464}
     1465
     1466// Waits for all in-flight fragments to finish, but doesn't end the call.
     1467errno_t async_call_wait(async_call_t *call)
     1468{
     1469        return async_call_wait_timeout(call, NULL);
     1470}
     1471
     1472errno_t async_call_wait_timeout(async_call_t *call, const struct timeval *expires)
     1473{
     1474        assert(call);
     1475
     1476        if (call->rc)
     1477                return call->rc;
     1478
     1479        /* Wait for all the fragments except the initial one. */
     1480        assert(list_first(&call->fragments) == &call->initial.link);
     1481        list_remove(&call->initial.link);
     1482
     1483        errno_t rc = async_call_finish_internal(call, expires);
     1484        list_prepend(&call->initial.link, &call->fragments);
     1485
     1486        if (rc == ETIMEOUT)
     1487                return rc;
     1488
     1489        /* If one fails, abort the call. */
     1490        if (rc != EOK)
     1491                async_call_abort(call);
     1492
     1493        call->rc = rc;
     1494        return rc;
     1495}
     1496
     1497void async_call_method_with_finalizer(async_call_t *call,
     1498    async_call_data_t *data, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2,
     1499    sysarg_t arg3, sysarg_t arg4, async_call_finalizer_t finalizer)
     1500{
     1501        assert(call);
     1502        assert(data);
     1503        data->finalizer = finalizer;
     1504
     1505        if (!call->exch)
     1506                call->rc = ENOENT;
     1507
     1508        if (call->rc)
     1509                return;
     1510
     1511        data->msgid = async_send_fast(call->exch, imethod,
     1512            arg1, arg2, arg3, arg4, &data->answer);
     1513        if (!data->msgid) {
     1514                async_call_abort(call);
     1515                call->rc = ENOMEM;
     1516        }
     1517
     1518        list_append(&data->link, &call->fragments);
     1519}
     1520
     1521void async_call_method(async_call_t *call, async_call_data_t *data,
     1522    sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
     1523{
     1524        assert(call);
     1525        assert(data);
     1526        memset(data, 0, sizeof(*data));
     1527
     1528        async_call_method_with_finalizer(call, data, imethod,
     1529            arg1, arg2, arg3, arg4, NULL);
     1530}
     1531
     1532static errno_t call_read_write_finalizer(async_call_data_t *data)
     1533{
     1534        size_t *sz = data->arg1;
     1535        if (sz)
     1536                *sz = IPC_GET_ARG2(data->answer);
     1537        return EOK;
     1538}
     1539
     1540void async_call_read(async_call_t *call, async_call_data_t *data,
     1541    void *dst, size_t size, size_t *nread)
     1542{
     1543        assert(call);
     1544        assert(data);
     1545        memset(data, 0, sizeof(*data));
     1546
     1547        data->arg1 = nread;
     1548
     1549        async_call_method_with_finalizer(call, data,
     1550            IPC_M_DATA_READ, (sysarg_t) dst, (sysarg_t) size, 0, 0,
     1551            call_read_write_finalizer);
     1552}
     1553
     1554/**
     1555 * After the call is successfully finished,
     1556 * `IPC_GET_ARG2(&data->answer)` holds the actual number of bytes written.
     1557 */
     1558void async_call_write(async_call_t *call, async_call_data_t *data,
     1559    const void *src, size_t size, size_t *nwritten)
     1560{
     1561        assert(call);
     1562        assert(data);
     1563        memset(data, 0, sizeof(*data));
     1564
     1565        data->arg1 = nwritten;
     1566
     1567        async_call_method_with_finalizer(call, data,
     1568            IPC_M_DATA_WRITE, (sysarg_t) src, (sysarg_t) size, 0, 0,
     1569            call_read_write_finalizer);
     1570}
     1571
     1572static errno_t call_share_in_finalizer(async_call_data_t *data)
     1573{
     1574        unsigned int *flags = data->arg1;
     1575        void **dst = data->arg2;
     1576
     1577        if (flags)
     1578                *flags = IPC_GET_ARG2(data->answer);
     1579
     1580        if (dst)
     1581                *dst = (void *) IPC_GET_ARG4(data->answer);
     1582
     1583        return EOK;
     1584}
     1585
     1586void async_call_share_in(async_call_t *call, async_call_data_t *data,
     1587    size_t size, sysarg_t arg, unsigned int *flags, void **dst)
     1588{
     1589        assert(call);
     1590        assert(data);
     1591        memset(data, 0, sizeof(*data));
     1592
     1593        data->arg1 = flags;
     1594        data->arg2 = dst;
     1595
     1596        async_call_method_with_finalizer(call, data,
     1597            IPC_M_SHARE_IN, (sysarg_t) size, (sysarg_t) arg, 0, 0,
     1598            call_share_in_finalizer);
     1599}
     1600
     1601void async_call_share_out(async_call_t *call, async_call_data_t *data,
     1602    void *src, unsigned int flags)
     1603{
     1604        async_call_method(call, data,
     1605            IPC_M_SHARE_OUT, (sysarg_t) src, 0, (sysarg_t) flags, 0);
     1606}
     1607
     1608// TODO: connect me to, connect to me, vfs handle, etc.
     1609
    13481610/** @}
    13491611 */
Note: See TracChangeset for help on using the changeset viewer.