diff --git a/README.devmap b/README.devmap index 9cf00897..91614a12 100644 --- a/README.devmap +++ b/README.devmap @@ -665,6 +665,111 @@ responses should be of: struct res_clm_trackstart +---------------------------------------------------------------------- +Using one file descriptor for async and sync requests at the same time +---------------------------------------------------------------------- + +A library may include async events but must also be able to handle +sync request/responses on the same fd. This is achieved via the +saRecvQueue() api call. + +1. First have a look at exec/amf.c::saAmfInitialize. + +This function creates a queue to store responses that are not to be +handled by the syncronous function, but instead meant to be handled by +the dispatch (async) function. + + /* + * An inq is needed to store async messages while waiting for a + * sync response + */ + error = saQueueInit (&amfInstance->inq, 512, sizeof (void *)); + if (error != SA_OK) { + goto error_put_destroy; + } + +2. Next have a look at exec/amf.c::saAmfProtectionGroupTrackStart. + +This function must ensure that it gets a particular response, even when +it may receive a request for a dispatch (async call). To solve this, +the function queues the message on amfInstance->inq. It will only +return a message in &req_amf_protectiongrouptrackstart once a message +with MESSAGE_RES_AMF_PROTECTIONGROUPTRACKSTART defined in header->id of +the response is received. + + error = saSendRetry (amfInstance->fd, +&req_amf_protectiongrouptrackstart, + sizeof (struct req_amf_protectiongrouptrackstart), +MSG_NOSIGNAL); + if (error != SA_OK) { + goto error_unlock; + } + +^^^^^^ This code sends the request + + error = saRecvQueue (amfInstance->fd, &message, + &amfInstance->inq, MESSAGE_RES_AMF_PROTECTIONGROUPTRACKSTART); + +^^^^^^^^ This is the API which waits for a particular +response. It will wait until a message with the header +MESSAGE_RES_AMF_PROTECTIONGROUPTRACKSTART is received. Any other +message it queues for the dispatch function to read the inq. + +3. Finally have a look at the exec/amf/saAmfDispatch function. + + saQueueIsEmpty(&amfInstance->inq, &empty); + if (empty == 0) { + /* + * Queue is not empty, read data from queue + */ + saQueueItemGet (&amfInstance->inq, (void *)&queue_msg); + msg = *queue_msg; + memcpy (&dispatch_data, msg, msg->size); + saQueueItemRemove (&amfInstance->inq); + } else { + /* + * Queue empty, read response from socket + */ + error = saRecvRetry (amfInstance->fd, &dispatch_data.header, + sizeof (struct message_header), MSG_WAITALL | +MSG_NOSIGNAL); + if (error != SA_OK) { + goto error_unlock; + } + if (dispatch_data.header.size > sizeof (struct +message_header)) { + error = saRecvRetry (amfInstance->fd, +&dispatch_data.data, + dispatch_data.header.size - sizeof (struct +message_header), + MSG_WAITALL | MSG_NOSIGNAL); + if (error != SA_OK) { + goto error_unlock; + } + } + } + +This code basically checks if the queue is empty, then reads from the +queue if there is a request, otherwise it reads from the socket. + +You might ask why doesn't the poll (not shown) block if there are +messages in the queue but none in the socket. It doesn't block because +every time a saRecvQueue queues a message, it sends a request to the +executive (activate poll) which then sends a dummy message back to the +library (activate poll) which keeps poll from blocking. The dummy +message is ignored by the dispatch function. + +Not a great approach (the activate poll stuff). I have an idea to fix +it though. Before a poll is ever done, the inq could be checked to see +if it is empty. If there are messages on the inq, the dispatch function +would not call poll, but instead indicate to the dispatch function to +dispatch messages. + +Fortunately most of this activate poll mess is hidden from the library +developer in saRecvQueue (this does the activate poll stuff). The +develoepr simply has to be aware that the activate poll message is +coming and ignore it appropriately. + ------------ some notes ------------