/** * callback associated with the socket controller for receiving a RPC message on a AF_UNIX socket operating of datagram mode @param socket_pointer: pointer to the socket context @param socketId: reference of the socket--> not used */ uint32_t af_unix_recv_rpc_stream_generic_cbk(void * socket_pointer,int socketId) { af_unix_ctx_generic_t *sock_p = (af_unix_ctx_generic_t*)socket_pointer; com_recv_template_t *recv_p; uint16_t recv_credit; void *buf_recv_p = NULL; int full_msg_len; uint32_t payloadLen; uint8_t *payload_p; uint32_t status; uint32_t *record_len_p; int len_read; void *bufref = NULL; com_rpc_recv_template_t *rpc; /* ** set the credit, notice that the credit is decremented upon reception ** of a full message */ recv_p = &sock_p->recv; recv_credit = recv_p->recv_credit_conf; rpc = &recv_p->rpc; while(recv_credit != 0) { switch (recv_p->state) { /* ** There is no recepition in progress */ case RECV_IDLE: rpc->last_record = 0; rpc->record_len = 0; rpc->in_tot_len = 0; rpc->in_wr_offset = 0; recv_p->nbread = 0; recv_p->nb2read = recv_p->headerSize; recv_p->bufRefCurrent = NULL; recv_p->state = RECV_WAIT_HDR; break; /* **_________________________________________________________________ ** Waiting for the header before allocating the receive buffer **_________________________________________________________________ */ case RECV_WAIT_HDR: /* ** attempt to receive the full header to figure out what kind of receive buffer ** Must be allocated */ status = af_unix_recv_stream_sock_recv(sock_p,recv_p->buffer_header+recv_p->nbread, recv_p->nb2read- recv_p->nbread ,0,&len_read); switch(status) { case RUC_OK: /* ** that's fine : go to the next step to figure out what kind of buffer can be allocated */ sock_p->stats.totalRecvBytes += len_read; if (recv_p->bufRefCurrent == NULL) { /* ** buffer has not be yet allocated */ recv_p->state = RECV_ALLOC_BUF; } else { /* ** Buffer has already been alllocated, so it is not the fisrt received record ** get the header of the RPC record in order to extract the length and the type ** of the rpc message. ** The current length of the rpc record is added with the total length. ** Since it is not the first record, the system does not store in the receive ** buffer the first 4 bytes of the rpc record. This is done for the first ** record only. */ record_len_p = (uint32_t *)recv_p->buffer_header; rpc->record_len = ntohl(*record_len_p); if (rpc->record_len & (~0x7fffffff)) { rpc->last_record = 1; } rpc->record_len &= 0x7fffffff; rpc->in_tot_len += rpc->record_len; if ((rpc->in_tot_len+recv_p->headerSize) > rpc->max_receive_sz) { /* ** release the buffer */ bufref = recv_p->bufRefCurrent; recv_p->bufRefCurrent = NULL; ruc_buf_freeBuffer(bufref); /* ** general disconnection */ af_unix_sock_stream_disconnect_internal(sock_p); recv_p->state = RECV_DEAD; sock_p->stats.totalRecvBadHeader++; /* ** the length is wrong, we have no choice we need to close the connection */ (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno); return TRUE; } /* ** set the number of bytes to read and already read */ recv_p->nbread = 0; recv_p->nb2read = rpc->record_len; /* ** now read the payload of the record */ recv_p->state = RECV_PAYLOAD; } break; case RUC_WOULDBLOCK: /* ** we don't get the full header so no change, wait for the next receiver event */ sock_p->stats.emptyRecv++; return TRUE; case RUC_PARTIAL: /* ** update the count and re-attempt until getting a EAGAIN or a full header */ sock_p->stats.partialRecv++; sock_p->stats.totalRecvBytes += len_read; recv_p->nbread += len_read; break; default: case RUC_DISC: /* ** general disconnection */ af_unix_sock_stream_disconnect_internal(sock_p); /* ** socket is dead call the user callback */ recv_p->state = RECV_DEAD; // warning("af_unix_recv_stream_generic_cbk:%s",strerror(errno)); (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno); return TRUE; } break; /* **_________________________________________________________________ ** allocate a receive buffer according to the length of the message **_________________________________________________________________ */ case RECV_ALLOC_BUF: /* ** store the total length of the message */ record_len_p = (uint32_t *)recv_p->buffer_header; rpc->record_len = ntohl(*record_len_p); if (rpc->record_len & (~0x7fffffff)) rpc->last_record = 1; rpc->record_len &= 0x7fffffff; /* ** check if the message does not exceed the max buffer size */ if (rpc->last_record == 0) { /* ** assume max length */ full_msg_len = rpc->max_receive_sz; } else { full_msg_len = rpc->record_len + recv_p->headerSize; } if (full_msg_len > rpc->max_receive_sz) { /* ** general disconnection: purge the xmit side */ af_unix_sock_stream_disconnect_internal(sock_p); recv_p->state = RECV_DEAD; sock_p->stats.totalRecvBadHeader++; /* ** the length is wrong, we have no choice we need to close the connection */ (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno); return TRUE; } /* ** Ok now call the application for receive buffer allocation */ if (sock_p->userRcvAllocBufCallBack != NULL) { buf_recv_p = (sock_p->userRcvAllocBufCallBack)(sock_p->userRef,sock_p->index,full_msg_len); } else { fatal("Alloc Buffer callback is mandatory for rpc service"); } if (buf_recv_p == NULL) { /* ** the receiver is out of buffer-> leave the message in the receiver queue and exit */ sock_p->stats.totalRecvOutoFBuf++; recv_p->state = RECV_ALLOC_BUF; return TRUE; } /* ** set the payload length in the buffer */ rpc->in_tot_len += rpc->record_len; payloadLen = rpc->record_len; ruc_buf_setPayloadLen(buf_recv_p,(uint32_t)(payloadLen + recv_p->headerSize)); /* ** Ok now start receiving the payload */ recv_p->nbread = recv_p->headerSize; recv_p->nb2read = recv_p->headerSize+payloadLen; recv_p->bufRefCurrent = buf_recv_p; payload_p = (uint8_t*)ruc_buf_getPayload(recv_p->bufRefCurrent); /* ** copy the already received bytes in the allocated received buffer */ memcpy(payload_p,recv_p->buffer_header,recv_p->headerSize); recv_p->state = RECV_PAYLOAD; break; /* **_________________________________________________________________ ** reception of the payload of the message **_________________________________________________________________ */ case RECV_PAYLOAD: /* ** attempt to receive the full header to figure out what kind of receive buffer ** Must be allocated */ payload_p = (uint8_t*)ruc_buf_getPayload(recv_p->bufRefCurrent); status = af_unix_recv_stream_sock_recv(sock_p,payload_p+rpc->in_wr_offset+recv_p->nbread, recv_p->nb2read- recv_p->nbread ,0,&len_read); switch(status) { case RUC_OK: /* ** update the speculative scheduler */ ruc_sockCtrl_speculative_scheduler_decrement(sock_p->connectionId); /* ** that fine's call the application with that received message */ sock_p->stats.totalRecvBytes += len_read; sock_p->stats.totalRecvSuccess++; recv_p->nbread += len_read; /* ** Check if it is the last record: in such a case we deliver the rpc message ** to the application */ if (rpc->last_record) { /* ** clear the reference of the buffer to avoid a double release that may occur ** if the application delete the context ** Update the first length header of the rpc message with the total length of the ** RPC message */ bufref = recv_p->bufRefCurrent; recv_p->bufRefCurrent = NULL; ruc_buf_setPayloadLen(bufref,(uint32_t)(rpc->in_tot_len + recv_p->headerSize)); record_len_p = (uint32_t *)payload_p; *record_len_p = htonl((rpc->in_tot_len) | 0x80000000); (sock_p->userRcvCallBack)(sock_p->userRef,sock_p->index,bufref); recv_p->state = RECV_IDLE; recv_credit--; break; } /* ** not the last record: ** udpate the in_wr_offset with the current record length and prepare to receive ** the next record */ rpc->in_wr_offset+= recv_p->nbread; recv_p->nbread = 0; recv_p->nb2read = recv_p->headerSize; recv_p->state = RECV_WAIT_HDR; break; case RUC_WOULDBLOCK: /* ** we don't get the full message so no change, wait for the next receiver event */ sock_p->stats.emptyRecv++; return TRUE; case RUC_PARTIAL: /* ** update the count and re-attempt until getting a EAGAIN or a full header */ sock_p->stats.partialRecv++; sock_p->stats.totalRecvBytes += len_read; recv_p->nbread += len_read; break; case RUC_DISC: default: /* ** socket is dead call the user callback */ bufref = recv_p->bufRefCurrent; recv_p->bufRefCurrent = NULL; ruc_buf_freeBuffer(bufref); /* ** general disconnection */ af_unix_sock_stream_disconnect_internal(sock_p); /* ** it is up to the application to release the buffer if the error is fatal ** but for the case of the receiver, no buffer reference is provided */ recv_p->state = RECV_DEAD; (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno); return TRUE; } break; /* **_________________________________________________________________ ** Dead state of the receiver **_________________________________________________________________ */ case RECV_DEAD: return TRUE; } } return TRUE; }
/* **__________________________________________________________________________ */ void af_unix_send_stream_fsm(af_unix_ctx_generic_t *socket_p,com_xmit_template_t *xmit_p) { char *pbuf; int write_len; int ret; int inuse; while(1) { switch (xmit_p->state) { case XMIT_READY: /* ** the transmitter is ready to send however we need to double if there is a ** current buffer to send (because we just exit from congestion or if there ** some buffer in the xmit pending queue */ /* ** Check if there is a current buffer to send */ if (xmit_p->bufRefCurrent != NULL) { write_len = (int)ruc_buf_getPayloadLen(xmit_p->bufRefCurrent); /* ** Get the reference of the destination socket (name) from the ruc_buffer) */ xmit_p->nbWrite = 0; xmit_p->nb2Write = write_len; xmit_p->state = XMIT_IN_PRG; } else { /* ** nothing to send !! */ return; } break; case XMIT_IN_PRG: /* ** Check if there is a current buffer to send */ socket_p->stats.totalXmitAttempts++; pbuf = (char *)ruc_buf_getPayload(xmit_p->bufRefCurrent); ret = af_unix_send_stream_generic(socket_p->socketRef,pbuf+xmit_p->nbWrite,xmit_p->nb2Write - xmit_p->nbWrite, &write_len); switch (ret) { case RUC_OK: /* ** release the buffer that has been sent */ xmit_p->xmit_credit++; inuse = ruc_buf_inuse_decrement(xmit_p->bufRefCurrent); if (inuse < 0) { /* ** inuse MUST never be negative so EXIT !!!!! */ fatal("Inuse is negative %d",inuse); } if (socket_p->userXmitDoneCallBack != NULL) { /* ** caution: in that case it is up to the application that provides the callback to release ** the xmit buffer */ if (ruc_buf_get_opaque_ref(xmit_p->bufRefCurrent) == socket_p) { (socket_p->userXmitDoneCallBack)(socket_p->userRef,socket_p->index,xmit_p->bufRefCurrent); } else { if (inuse == 1) { /* ** need an obj remove since that buffer might still queue somewhere : typically ** in the xmit list of a load balacner entry. */ ruc_objRemove((ruc_obj_desc_t*)xmit_p->bufRefCurrent); ruc_buf_freeBuffer(xmit_p->bufRefCurrent); } } } else { if (inuse == 1) { ruc_objRemove((ruc_obj_desc_t*)xmit_p->bufRefCurrent); ruc_buf_freeBuffer(xmit_p->bufRefCurrent); } } xmit_p->bufRefCurrent = NULL; xmit_p->nbWrite = 0; xmit_p->nb2Write = 0; socket_p->stats.totalXmitSuccess++; socket_p->stats.totalXmitBytes += write_len; xmit_p->state = XMIT_CHECK_XMITQ; break; case RUC_PARTIAL: /* ** need to re-attempt writing */ xmit_p->nbWrite += write_len; socket_p->stats.totalXmitBytes += write_len; break; case RUC_WOULDBLOCK: /* ** the socket is congested-> so exit */ socket_p->stats.totalXmitCongested++; xmit_p->congested_flag = 1; xmit_p->eoc_flag = 0; xmit_p->eoc_threshold = AF_UNIX_CONGESTION_DEFAULT_THRESHOLD; xmit_p->state = XMIT_CONGESTED; return ; case RUC_DISC: /* ** something wrong on sending: if the user has a callback use it: ** the transmitter is no more the owner of the buffer */ inuse = ruc_buf_inuse_decrement(xmit_p->bufRefCurrent); if (inuse < 0) { /* * inuse MUST never be negative so EXIT !!!!! */ fatal("Inuse is negative %d",inuse); } socket_p->stats.totalXmitError++; if (socket_p->userDiscCallBack != NULL) { void *bufref = xmit_p->bufRefCurrent; xmit_p->bufRefCurrent = NULL; if (ruc_buf_get_opaque_ref(bufref) != socket_p) { /* ** the buffer is affected to another socket, however it might possible ** that the real owner of the buffer has finished while the buffer is ** still used by that old connection. So it might be necessary to release ** the buffer. ** However in any case the application must not be inform that there was ** an issue while sendig that buffer since the connection is not considered ** anymore. */ if (inuse == 1) { ruc_objRemove((ruc_obj_desc_t*)bufref); ruc_buf_freeBuffer(bufref); } bufref = NULL; } /* ** it is up to the application to release the buffer if the error is fatal: ** caution the internal disconnection MUST be called before the application since ** the application might attempt to perform a direct re-connection */ xmit_p->state = XMIT_DEAD; af_unix_sock_stream_disconnect_internal(socket_p); (socket_p->userDiscCallBack)(socket_p->userRef,socket_p->index,bufref,errno); return; } else { if (inuse == 1) { ruc_objRemove((ruc_obj_desc_t*)xmit_p->bufRefCurrent); ruc_buf_freeBuffer(xmit_p->bufRefCurrent); } xmit_p->bufRefCurrent = NULL; } /* ** general disconnection->need to clean the socket queue */ xmit_p->state = XMIT_DEAD; af_unix_sock_stream_disconnect_internal(socket_p); return ; break; } break; case XMIT_CHECK_XMITQ: /* ** Check the xmit credit */ if (xmit_p->xmit_credit >= xmit_p->xmit_credit_conf) { xmit_p->xmit_credit = 0; /* ** asser the flag to request a re-activation on the next run of the socket ** controller */ xmit_p->xmit_req_flag = 1; return; } /* ** check if there is a pending buffer (case found if there was a previous congestion */ if (xmit_p->bufRefCurrent != NULL) { /* * lest's go and send it */ xmit_p->state = XMIT_IN_PRG; break; } /* ** read the pending Xmit queue (only priority 0 is considered in the current implementation */ xmit_p->bufRefCurrent = com_xmit_pendingQueue_get(xmit_p,0); if (xmit_p->bufRefCurrent == NULL) { /* ** queue is empty */ xmit_p->xmit_credit = 0; xmit_p->state = XMIT_READY; return; } /* ** OK, go back to send that new bufffer */ ruc_buf_inuse_increment(xmit_p->bufRefCurrent); xmit_p->state = XMIT_READY; break; case XMIT_CONGESTED: /* ** the transmitter is congested: check of the threshold has reached 0 */ xmit_p->eoc_threshold--; if (xmit_p->eoc_threshold == 0) { xmit_p->eoc_flag = 1; xmit_p->congested_flag = 0; xmit_p->state = XMIT_IN_PRG; break; } return; case XMIT_DEAD: /* ** the transmitter is dead */ return; } } }
/** * callback associated with the socket controller for receiving a message on a AF_UNIX socket operating of datagram mode @param socket_pointer: pointer to the socket context @param socketId: reference of the socket--> not used */ uint32_t af_unix_recv_stream_generic_cbk(void * socket_pointer,int socketId) { af_unix_ctx_generic_t *sock_p = (af_unix_ctx_generic_t*)socket_pointer; com_recv_template_t *recv_p; uint16_t recv_credit; void *buf_recv_p = NULL; int full_msg_len; uint32_t payloadLen; uint8_t *payload_p; uint32_t status; int len_read; void *bufref = NULL; /* ** set the credit, notice that the credit is decremented upon reception ** of a full message */ recv_p = &sock_p->recv; recv_credit = recv_p->recv_credit_conf; while(recv_credit != 0) { switch (recv_p->state) { /* ** There is no recepition in progress */ case RECV_IDLE: recv_p->nbread = 0; recv_p->nb2read = recv_p->headerSize; recv_p->bufRefCurrent = NULL; recv_p->state = RECV_WAIT_HDR; break; /* **_________________________________________________________________ ** Waiting for the header before allocating the receive buffer **_________________________________________________________________ */ case RECV_WAIT_HDR: /* ** attempt to receive the full header to figure out what kind of receive buffer ** Must be allocated */ status = af_unix_recv_stream_sock_recv(sock_p,recv_p->buffer_header+recv_p->nbread, recv_p->nb2read- recv_p->nbread ,0,&len_read); switch(status) { case RUC_OK: /* ** that's fine : go to the next step to figure out what kind of buffer can be allocated */ sock_p->stats.totalRecvBytes += len_read; recv_p->state = RECV_ALLOC_BUF; break; case RUC_WOULDBLOCK: /* ** we don't get the full header so no change, wait for the next receiver event */ sock_p->stats.emptyRecv++; return TRUE; case RUC_PARTIAL: /* ** update the count and re-attempt until getting a EAGAIN or a full header */ sock_p->stats.partialRecv++; sock_p->stats.totalRecvBytes += len_read; recv_p->nbread += len_read; break; default: case RUC_DISC: /* ** general disconnection */ af_unix_sock_stream_disconnect_internal(sock_p); /* ** socket is dead call the user callback */ recv_p->state = RECV_DEAD; // warning("af_unix_recv_stream_generic_cbk: %s",strerror(errno)); (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno); return TRUE; } break; /* **_________________________________________________________________ ** allocate a receive buffer according to the length of the message **_________________________________________________________________ */ case RECV_ALLOC_BUF: if (sock_p->userHdrAnalyzerCallBack != NULL) { /* ** The applicaton has provide a callback for parsing its header and to extract the ** length of the payload-> Typically, it is mandatory for the case of the RPC since ** the field that contains the length has the bit 31 asserted */ payloadLen = (sock_p->userHdrAnalyzerCallBack)((char*)recv_p->buffer_header); } else { payloadLen = com_sock_extract_length_from_header_host_format((char*)recv_p->buffer_header, recv_p->msgLenOffset, recv_p->msgLenSize); } if (payloadLen == 0) { /* ** general disconnection */ af_unix_sock_stream_disconnect_internal(sock_p); recv_p->state = RECV_DEAD; sock_p->stats.totalRecvBadHeader++; /* ** the length is wrong, we have no choice we need to close the connection */ (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno); return TRUE; } /* ** check if the message does not exceed the max buffer size */ full_msg_len = payloadLen + recv_p->headerSize; if (full_msg_len > recv_p->bufSize) { /* ** general disconnection */ af_unix_sock_stream_disconnect_internal(sock_p); recv_p->state = RECV_DEAD; sock_p->stats.totalRecvBadHeader++; /* ** the length is wrong, we have no choice we need to close the connection */ (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno); return TRUE; } /* ** Ok now call the application for receive buffer allocation */ if (sock_p->userRcvAllocBufCallBack != NULL) { buf_recv_p = (sock_p->userRcvAllocBufCallBack)(sock_p->userRef,sock_p->index,full_msg_len); } else { buf_recv_p = af_unix_alloc_recv_buf(); } if (buf_recv_p == NULL) { /* ** the receiver is out of buffer-> leave the message in the receiver queue and exit */ sock_p->stats.totalRecvOutoFBuf++; recv_p->state = RECV_ALLOC_BUF; return TRUE; } /* ** set the payload length in the buffer */ ruc_buf_setPayloadLen(buf_recv_p,(uint32_t)(payloadLen + recv_p->headerSize)); /* ** Ok now start receiving the payload */ recv_p->nbread = recv_p->headerSize; recv_p->nb2read = recv_p->headerSize+payloadLen; recv_p->bufRefCurrent = buf_recv_p; payload_p = (uint8_t*)ruc_buf_getPayload(recv_p->bufRefCurrent); /* ** copy the already received bytes in the allocated received buffer */ memcpy(payload_p,recv_p->buffer_header,recv_p->headerSize); recv_p->state = RECV_PAYLOAD; break; /* **_________________________________________________________________ ** reception of the payload of the message **_________________________________________________________________ */ case RECV_PAYLOAD: /* ** attempt to receive the full header to figure out what kind of receive buffer ** Must be allocated */ payload_p = (uint8_t*)ruc_buf_getPayload(recv_p->bufRefCurrent); status = af_unix_recv_stream_sock_recv(sock_p,payload_p+recv_p->nbread, recv_p->nb2read- recv_p->nbread ,0,&len_read); switch(status) { case RUC_OK: /* ** update the speculative scheduler */ ruc_sockCtrl_speculative_scheduler_decrement(sock_p->connectionId); /* ** that fine's call the application with that received message */ sock_p->stats.totalRecvBytes += len_read; sock_p->stats.totalRecvSuccess++; /* ** clear the reference of the buffer to avoid a double release that may occur ** if the application delete the context */ bufref = recv_p->bufRefCurrent; recv_p->bufRefCurrent = NULL; (sock_p->userRcvCallBack)(sock_p->userRef,sock_p->index,bufref); recv_p->state = RECV_IDLE; recv_credit--; break; case RUC_WOULDBLOCK: /* ** we don't get the full message so no change, wait for the next receiver event */ sock_p->stats.emptyRecv++; return TRUE; case RUC_PARTIAL: /* ** update the count and re-attempt until getting a EAGAIN or a full header */ sock_p->stats.partialRecv++; sock_p->stats.totalRecvBytes += len_read; recv_p->nbread += len_read; break; case RUC_DISC: default: /* ** socket is dead call the user callback */ bufref = recv_p->bufRefCurrent; recv_p->bufRefCurrent = NULL; ruc_buf_freeBuffer(bufref); /* ** general disconnection */ af_unix_sock_stream_disconnect_internal(sock_p); /* ** it is up to the application to release the buffer if the error is fatal ** but for the case of the receiver, no buffer reference is provided */ recv_p->state = RECV_DEAD; (sock_p->userDiscCallBack)(sock_p->userRef,sock_p->index,NULL,errno); return TRUE; } break; /* **_________________________________________________________________ ** Dead state of the receiver **_________________________________________________________________ */ case RECV_DEAD: return TRUE; } } return TRUE; }