Unblocking if someone closes a file descriptor

Suppose the following sequence occurs:

If your resource manager doesn't handle this case, then the client's read() is indefinitely blocked, even though the fd has been detached. This could happen for other blocking operations, such as calls to write() and devctl().

To avoid this situation, your resource manager needs to maintain a list of blocked clients. Whenever it blocks a client that's waiting for an operation to complete, you must add this client to the list—and you need to remove a client from the list when you unblock it.

The resource manager framework provides an io_close_dup() callout that gets called whenever close() is called on a file descriptor (see the Resource Managers chapter of Getting Started with QNX Neutrino). In this callout, you must traverse the list and use the server connection ID (scoid) and connection ID (coid) to determine which clients (be they separate processes or threads) are blocked on that file descriptor, and reply to these clients (via MsgError() or otherwise) to unblock them. For example:

int io_close_dup (resmgr_context_t *ctp, io_close_t *msg, RESMGR_OCB_T *ocb)
{
    // unblock any clients blocked on the file descriptor being closed
    blocked_client_t *client, *prev;
    prev = NULL;
    client = blocked_clients;
    while ( client != NULL )
    {
        if ( (client->coid == ctp->info.coid) && (client->scoid == ctp->info.scoid) )
        {
            MsgError( client -> rcvid, EBADF );
            if (prev != NULL) // anywhere but the head of the list
            {
                prev->next = client->next;
                free(client);
                client = prev->next;
            }
            else // head of the list case
            {
                blocked_clients = client->next; 
                free(client); 
                client = blocked_clients; 
            }
        }
        else // no match, move to the next entry and track previous entry
        { 
            prev = client;
            client = client->next;
        }
    }
    // do rest of the regular close handling
    return iofunc_close_dup_default(ctp, msg, ocb );
}