Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changes between Version 5 and Version 6 of AsyncSessions


Ignore:
Timestamp:
2018-06-21T14:45:40Z (10 months ago)
Author:
Martin Decky
Comment:

update for current async framework API

Legend:

Unmodified
Added
Removed
Modified
  • AsyncSessions

    v5 v6  
    22
    33== The problem ==
     4The functions of the low-level IPC mechanism of HelenOS that perform IPC requests (such as ipc_call_async_0()) operate on phones (they are passed a phone capability handle). In any non-trivial IPC protocol there are operations that consist of more than one IPC call (e.g. VFS_IN_READ) performed either in parallel or in series. (Here we call an operation a set of IPC calls that causes the server to perform a well-defined action). For such protocol it is not possible to perform several operations concurrently on the same phone, because the calls from different operations would get mixed up.
    45
    5 Traditionally, functions of the Async framework that perform requests (such as async_req()) operate on phones (they are passed a phone ID). In any non-trivial IPC protocol there are operations that consist of more than one IPC call (e.g. VFS_IN_READ) performed either in parallel or in series. (Here we call an operation a set of IPC calls that causes the server to perform a well-defined action). For such protocol it is not possible to perform several operations concurrently on the same phone, because the calls from different operations would get mixed up.
    6 
    7 For illustration, consider the following client API stub functions:
     6For illustration, consider the following client API pseudocode:
    87
    98{{{
    10 int my_phone;
     9int phone;
    1110
    12 int mysrv_connect(void)
     11int server_connect(void)
    1312{
    1413    /* Connect phone to the service */
    15     my_phone = async_connect_me_to(PHONE_NS, SERVICE_MYSRV, 0);
     14    phone = ipc_connect_me_to_iface(PHONE_NS, INTERFACE_SOME_SRV, SERVICE_SOME_SRV, 0);
    1615}
    1716
    18 int mysrv_read(int arg, void *buf, size_t bufsize)
     17int server_read(int arg, void *buf, size_t bufsize)
    1918{
    2019    /* Send first request R1 (including operation code). */
    21     req = async_send_1(my_phone, MYSRV_READ, arg);
     20    ipc_call_async_1(phone, SOME_SRV_READ, NULL, server_read_callback);
    2221
    23     /* Send second request (IPC_READ) and wait for result. */
    24     rc = async_data_read_start(my_phone, buf, bufsize);
    25     if (rc != EOK) {
    26         /* handle error */
    27     }
     22    /* Send second request (IPC_M_DATA_READ) */
     23    ipc_data_read_start(phone, buf, bufsize, NULL, server_read_data_callback);
    2824
    29     /* Wait for R1 reply. */
    30     async_wait_for(req, &rc);
    31     if (rc != EOK) {
    32         /* handle error */
    33     }
     25    /* Wait for the callbacks to be called */
    3426}
    3527}}}
    3628
    37 Were we to call mysrv_read() from two different fibrils A and B concurrently, the server fibril would see some mixture of the sequence (AR1, AR2) with (BR1, BR2), e.g. (AR1, BR1, AR2, BR2) which would be incorrect from the server point of view. Note that the server has no way of telling which request belongs to which operation.
     29Were we to call ''server_read()'' from two different threads or fibrils A and B concurrently, the server fibril would see some mixture of the sequence (AR1, AR2) with (BR1, BR2), e.g. (AR1, BR1, AR2, BR2), which would be incorrect from the server point of view. Note that the server has no way of telling which request belongs to which operation.
    3830
    39 We often want to allow such function to be called concurrently in different fibrils. mysrv_read() can block so we cannot serialize calls to it. If we took a character device as an example, we often want to allow one fibril to write while another fibril is blocked in a read operation.
     31We often want to allow such function to be called concurrently in different threads or fibrils. ''server_read()'' can block so we cannot serialize calls to it. If we took a character device as an example, we often want to allow one fibril to write while another fibril is blocked in a read operation.
    4032
    4133== Enter sessions ==
    4234
    43 A '''session''' (cz: relace) is a ''logical'' datapath from a client to a server which can support ''concurrent'' operations. Each operation is performed in the context of an '''exchange''' (cz: výměna). An exchange can support an IPC operation (or, ''optionally'', a sequence of operations), but, in any case, only one operation is performed in an exchange at any time. (The ''session''/''connection'' terminology is borrowed from iSCSI and ''exchange'' is borrowed from Fibre Channel transport layer).
     35A '''session''' is a ''logical'' datapath from a client to a server which can support ''concurrent'' operations. Each operation is performed in the context of an '''exchange'''. An exchange can support an IPC operation (or, ''optionally'', a sequence of operations), but, in any case, only one operation is performed in an exchange at any time. (The ''session''/''connection'' terminology is borrowed from iSCSI and ''exchange'' is borrowed from Fibre Channel transport layer).
    4436
    45 A client first needs to create a session (to a server) by calling `async_session_create()`. Whenever the client needs to perform an operation, it starts an exchange by calling `async_exchange_begin()` and performs the operation in context of that exchange. Then the client ends the exchange by calling `async_exchange_end()`.
     37The async framework abstracts from explicitly creating sessions by wrapping the low-level phone handles into ''async_sess_t'' automatically (by the means if calling ''async_connect_me_to_iface()'', ''async_callback_receive()'', etc.). Whenever the client needs to perform an operation, it starts an exchange by calling `async_exchange_begin()` and performs the operation in context of that exchange. Then the client ends the exchange by calling `async_exchange_end()`.
    4638
    47 We can ammend the example above as follows:
     39We can amend the example above as follows:
    4840
    4941{{{
    50 async_sess_t my_session;
     42async_sess_t *session;
    5143
    52 int mysrv_connect()
     44int server_connect()
    5345{
    54     int phone;
     46    /* Create an exchange on the naming service session */
     47    async_exch_t *exch = async_exchange_begin(ns_session);
    5548
    56     /* Connect phone to the service */
    57     phone = async_connect_me_to(PHONE_NS, SERVICE_MYSRV, 0);
     49    /* Connect a session to the service */
     50    session = async_connect_me_to_iface(exch, INTERFACE_SOME_SRV, SERVICE_SOME_SRV, 0);
    5851
    59     /* Create session */
    60     async_session_create(&my_session, phone);
     52    /* End the naming service exchange */
     53    async_exchange_end(exch);
    6154}
    6255
    63 int mysrv_read(int arg, void *buf, size_t bufsize)
     56int server_read(int arg, void *buf, size_t bufsize)
    6457{
    65     int phone;
    66 
    67     /* Begin exchange */
    68     phone = async_exchange_begin(&my_session);
     58    /* Begin the exchange */
     59    async_exch_t *exch = async_exchange_begin(session);
    6960
    7061    /* Send first request R1 (including operation code). */
    71     req = async_send_1(phone, MYSRV_READ, arg);
     62    aid_t req = async_send_1(exch, SOME_SRV_READ, arg);
    7263
    73     /* Send second request (IPC_READ) and wait for result. */
    74     rc = async_data_read_start(phone, buf, bufsize);
    75     if (rc != EOK) {
    76         /* handle error */
    77     }
     64    /* Send second request (IPC_M_DATA_READ) and wait for result. */
     65    async_data_read_start(exch, buf, bufsize);
     66
     67    /* End the exchange */
     68    async_exchange_end(exch);
    7869
    7970    /* Wait for R1 reply. */
     71    int rc;
    8072    async_wait_for(req, &rc);
    81     if (rc != EOK) {
    82         /* handle error */
    83     }
    84 
    85     /* End exchange */
    86     async_exchange_end(&my_session, phone);
    8773}
    8874}}}
    8975
    90 Now we can run mysrv_read() in parallel in any number of fibrils/threads. Each IPC request has a context (the exchange) so the different parallel operations will not mix up.
     76Now we can run server_read() in parallel in any number of fibrils/threads. Each IPC request has a context (the exchange) so the different parallel operations will not mix up.
    9177
    92 Behind the scenes the async framework will do the magic necessary to distinguish the exchanges. In the current implementation it clones the session phone and uses as many separate physical connections as there are concurrent exchanges. It caches the open connections to avoid setting them up and tearing them down too often.
    93 
    94 In a future implementation the async framework could instead embed an additional qualifier into each IPC request that would allow to distinguish requests from different exchanges (e.g. a virtual path ID).
     78Behind the scenes the async framework will do the magic necessary to distinguish the exchanges. Depending on the implementation of the server and the interface type, the async framework either enforces serialization of exchanges via locking or clones the session phone and uses as many separate physical connections as there are concurrent exchanges. It caches the open connections to avoid setting them up and tearing them down too often.