ASYNC_SOCKET_SEND_SYNC_RESULT async_socket_send_async()

in win32/src/async_socket_win32.c [436:650]


ASYNC_SOCKET_SEND_SYNC_RESULT async_socket_send_async(ASYNC_SOCKET_HANDLE async_socket, const ASYNC_SOCKET_BUFFER* buffers, uint32_t buffer_count, ON_ASYNC_SOCKET_SEND_COMPLETE on_send_complete, void* on_send_complete_context)
{
    ASYNC_SOCKET_SEND_SYNC_RESULT result;

    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_027: [ on_send_complete_context shall be allowed to be NULL. ]*/

    if (
        /* Codes_SRS_ASYNC_SOCKET_WIN32_01_024: [ If async_socket is NULL, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
        (async_socket == NULL) ||
        /* Codes_SRS_ASYNC_SOCKET_WIN32_01_025: [ If buffers is NULL, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
        (buffers == NULL) ||
        /* Codes_SRS_ASYNC_SOCKET_WIN32_01_085: [ If buffer_count is 0, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
        (buffer_count == 0) ||
        /* Codes_SRS_ASYNC_SOCKET_WIN32_01_026: [ If on_send_complete is NULL, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
        (on_send_complete == NULL)
        )
    {
        LogError("Invalid arguments: ASYNC_SOCKET_HANDLE async_socket=%p, const ASYNC_SOCKET_BUFFER* payload=%p, uint32_t buffer_count=%" PRIu32 ", ON_ASYNC_SOCKET_SEND_COMPLETE on_send_complete=%p, void*, on_send_complete_context=%p",
            async_socket, buffers, buffer_count, on_send_complete, on_send_complete_context);
        result = ASYNC_SOCKET_SEND_SYNC_ERROR;
    }
    else
    {
        // limit memory needed to UINT32_MAX
        if (buffer_count > (UINT32_MAX - sizeof(ASYNC_SOCKET_IO_CONTEXT)) / sizeof(WSABUF))
        {
            /* Codes_SRS_ASYNC_SOCKET_WIN32_01_103: [ If the amount of memory needed to allocate the context and the WSABUF items is exceeding UINT32_MAX, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
            LogError("Buffer count too big: %" PRIu32, buffer_count);
            result = ASYNC_SOCKET_SEND_SYNC_ERROR;
        }
        else
        {
            uint32_t i;
            uint32_t total_buffer_bytes = 0;

            for (i = 0; i < buffer_count; i++)
            {
                if (
                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_089: [ If any of the buffers in payload has buffer set to NULL, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
                    (buffers[i].buffer == NULL) ||
                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_090: [ If any of the buffers in payload has length set to 0, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
                    (buffers[i].length == 0)
                    )
                {
                    LogError("Invalid buffer %" PRIu32 ": buffer=%p, length = %" PRIu32, i, buffers[i].buffer, buffers[i].length);
                    break;
                }

                if (total_buffer_bytes + buffers[i].length < total_buffer_bytes)
                {
                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_101: [ If the sum of buffer lengths for all the buffers in payload is greater than UINT32_MAX, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
                    LogError("Overflow in total buffer length computation (total_buffer_bytes=%" PRIu32 " + buffers[i=%" PRIu32 "].length=%" PRIu32 "", total_buffer_bytes, i, buffers[i].length);
                    break;
                }
                else
                {
                    total_buffer_bytes += buffers[i].length;
                }
            }

            if (i < buffer_count)
            {
                LogError("Invalid buffers passed to async_socket_send_async");
                result = ASYNC_SOCKET_SEND_SYNC_ERROR;
            }
            else
            {
                (void)InterlockedIncrement(&async_socket->pending_api_calls);

                ASYNC_SOCKET_WIN32_STATE current_state;
                if ((current_state = InterlockedAdd(&async_socket->state, 0)) != (LONG)ASYNC_SOCKET_WIN32_STATE_OPEN)
                {
                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_097: [ If async_socket is not OPEN, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_NOT_OPEN. ]*/
                    LogWarning("Not open, current state is %" PRI_MU_ENUM "", MU_ENUM_VALUE(ASYNC_SOCKET_WIN32_STATE, current_state));
                    result = ASYNC_SOCKET_SEND_SYNC_NOT_OPEN;
                }
                else
                {
                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_028: [ Otherwise async_socket_send_async shall create a context for the send where the payload, on_send_complete and on_send_complete_context shall be stored. ]*/
                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_050: [ The context shall also allocate enough memory to keep an array of buffer_count WSABUF items. ]*/
                    ASYNC_SOCKET_IO_CONTEXT* send_context = malloc_flex(sizeof(ASYNC_SOCKET_IO_CONTEXT), buffer_count, sizeof(WSABUF));
                    if (send_context == NULL)
                    {
                        /* Codes_SRS_ASYNC_SOCKET_WIN32_01_029: [ If any error occurs, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
                        LogError("failure in malloc_flex(sizeof(ASYNC_SOCKET_IO_CONTEXT)=%zu, buffer_count=%" PRIu32 ", sizeof(WSABUF)=%zu) failed",
                            sizeof(ASYNC_SOCKET_IO_CONTEXT), buffer_count, sizeof(WSABUF));
                        result = ASYNC_SOCKET_SEND_SYNC_ERROR;
                    }
                    else
                    {
                        send_context->total_buffer_bytes = total_buffer_bytes;

                        /* Codes_SRS_ASYNC_SOCKET_WIN32_01_056: [ async_socket_send_async shall set the WSABUF items to point to the memory/length of the buffers in payload. ]*/
                        for (i = 0; i < buffer_count; i++)
                        {
                            send_context->wsa_buffers[i].buf = buffers[i].buffer;
                            send_context->wsa_buffers[i].len = buffers[i].length;
                        }

                        (void)memset(&send_context->overlapped, 0, sizeof(send_context->overlapped));

                        /* Codes_SRS_ASYNC_SOCKET_WIN32_01_057: [ An event to be used for the OVERLAPPED structure passed to WSASend shall be created and stored in the context. ]*/
                        send_context->overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
                        if (send_context->overlapped.hEvent == NULL)
                        {
                            /* Codes_SRS_ASYNC_SOCKET_WIN32_01_029: [ If any error occurs, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
                            LogLastError("CreateEvent failed");
                            result = ASYNC_SOCKET_SEND_SYNC_ERROR;
                        }
                        else
                        {
                            int wsa_send_result;
                            int wsa_last_error;

                            send_context->io_type = ASYNC_SOCKET_IO_TYPE_SEND;
                            send_context->io.send.on_send_complete = on_send_complete;
                            send_context->io.send.on_send_complete_context = on_send_complete_context;

                            /* Codes_SRS_ASYNC_SOCKET_WIN32_01_060: [ An asynchronous IO shall be started by calling StartThreadpoolIo. ]*/
                            StartThreadpoolIo(async_socket->tp_io);

#ifdef ENABLE_SOCKET_LOGGING
                            LogVerbose("Starting send of %" PRIu32 " bytes at %lf", total_buffer_bytes, timer_global_get_elapsed_us());
#endif

                            /* Codes_SRS_ASYNC_SOCKET_WIN32_01_061: [ The WSABUF array associated with the context shall be sent by calling WSASend and passing to it the OVERLAPPED structure with the event that was just created, dwFlags set to 0, lpNumberOfBytesSent set to NULL and lpCompletionRoutine set to NULL. ]*/
                            wsa_send_result = WSASend((SOCKET)async_socket->socket_handle, send_context->wsa_buffers, buffer_count, NULL, 0, &send_context->overlapped, NULL);

                            switch (wsa_send_result)
                            {
                                default:
                                {
                                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_106: [ If WSASend fails with any other error, async_socket_send_async shall call CancelThreadpoolIo and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
                                    LogLastError("WSASend failed with %d", wsa_send_result);
                                    result = ASYNC_SOCKET_SEND_SYNC_ERROR;

                                    break;
                                }
                                case SOCKET_ERROR:
                                {
                                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_062: [ If WSASend fails, async_socket_send_async shall call WSAGetLastError. ]*/
                                    wsa_last_error = WSAGetLastError();

                                    switch (wsa_last_error)
                                    {
                                        default:
                                        {
                                            /* Codes_SRS_ASYNC_SOCKET_WIN32_01_029: [ If any error occurs, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_ERROR. ]*/
                                            LogLastError("WSASend failed with %d, WSAGetLastError returned %lu", wsa_send_result, (unsigned long)wsa_last_error);
                                            result = ASYNC_SOCKET_SEND_SYNC_ERROR;

                                            break;
                                        }
                                        case WSAECONNRESET:
                                        {
                                            /* Codes_SRS_ASYNC_SOCKET_WIN32_42_002: [ If WSAGetLastError returns WSAECONNRESET, async_socket_send_async shall fail and return ASYNC_SOCKET_SEND_SYNC_NOT_OPEN. ]*/
                                            LogLastError("WSASend failed with %d, WSAGetLastError returned %lu", wsa_send_result, (unsigned long)wsa_last_error);
                                            result = ASYNC_SOCKET_SEND_SYNC_NOT_OPEN;

                                            break;
                                        }
                                        case WSA_IO_PENDING:
                                        {
                                            /* Codes_SRS_ASYNC_SOCKET_WIN32_01_053: [ If WSAGetLastError returns WSA_IO_PENDING, it shall be not treated as an error. ]*/
                                            result = ASYNC_SOCKET_SEND_SYNC_OK;

                                            break;
                                        }
                                    }
                                    break;
                                }
                                case 0:
                                {
                                    /* Codes_SRS_ASYNC_SOCKET_WIN32_01_045: [ On success, async_socket_send_async shall return ASYNC_SOCKET_SEND_SYNC_OK. ]*/
#ifdef ENABLE_SOCKET_LOGGING
                                    LogVerbose("Send completed synchronously at %lf", timer_global_get_elapsed_us());
#endif
                                    result = ASYNC_SOCKET_SEND_SYNC_OK;

                                    break;
                                }
                            }

                            if (result != ASYNC_SOCKET_SEND_SYNC_OK)
                            {
                                /* Codes_SRS_ASYNC_SOCKET_WIN32_01_100: [ If WSAGetLastError returns any other error, async_socket_send_async shall call CancelThreadpoolIo. ]*/
                                CancelThreadpoolIo(async_socket->tp_io);
                            }
                            else
                            {
                                (void)InterlockedDecrement(&async_socket->pending_api_calls);
                                WakeByAddressSingle((PVOID)&async_socket->pending_api_calls);

                                goto all_ok;
                            }

                            if (!CloseHandle(send_context->overlapped.hEvent))
                            {
                                LogLastError("CloseHandle failed");
                            }
                        }

                        free(send_context);
                    }
                }

                (void)InterlockedDecrement(&async_socket->pending_api_calls);
                WakeByAddressSingle((PVOID)&async_socket->pending_api_calls);
            }
        }
    }

all_ok:
    return result;
}