Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The request never returns #239

Open
paopaol opened this issue Nov 6, 2024 · 3 comments
Open

The request never returns #239

paopaol opened this issue Nov 6, 2024 · 3 comments

Comments

@paopaol
Copy link

paopaol commented Nov 6, 2024

When a request is sent and a response is waiting, another thread calls the delete route. Then the request will be blocked forever. After analyzing the code, it seems that the Notify of the response will never be called because the Recv method of AmsConnection will throw an exception.

void AmsConnection::Recv()
{
    AmsTcpHeader amsTcpHeader;
    AoEHeader aoeHeader;
    for ( ; ownIp; ) {
       // Receive maybe throw expection.when socket shutdown called.
       //then the response Notify never called
        Receive(amsTcpHeader);
        if (amsTcpHeader.length() < sizeof(aoeHeader)) {
            LOG_WARN("Frame to short to be AoE");
            ReceiveJunk(amsTcpHeader.length());
            continue;
        }

        Receive(aoeHeader);
        if (aoeHeader.cmdId() == AoEHeader::DEVICE_NOTIFICATION) {
            ReceiveNotification(aoeHeader);
            continue;
        }

        auto response = GetPending(aoeHeader.invokeId(), aoeHeader.targetPort());
        if (!response) {
            LOG_WARN("No response pending");
            ReceiveJunk(aoeHeader.length());
            continue;
        }

        switch (aoeHeader.cmdId()) {
        case AoEHeader::READ_DEVICE_INFO:
        case AoEHeader::WRITE:
        case AoEHeader::READ_STATE:
        case AoEHeader::WRITE_CONTROL:
        case AoEHeader::ADD_DEVICE_NOTIFICATION:
        case AoEHeader::DEL_DEVICE_NOTIFICATION:
            ReceiveFrame<AoEResponseHeader>(response, aoeHeader.length(), aoeHeader.errorCode());
            continue;
@pbruenn
Copy link
Member

pbruenn commented Nov 20, 2024

Where exactly? Until GetPending() the response is not marked as in use so the request will timeout. And after GetPending() I see no code path without Notify.

@paopaol
Copy link
Author

paopaol commented Nov 28, 2024

Where exactly? Until GetPending() the response is not marked as in use so the request will timeout. And after GetPending() I see no code path without Notify.

What does it mean that the response is not marked as in use? I checked the code and found that when the data is sent out, the response will wait on a condition variable. But if Receive(amsTcpHeader) throws an exception, then GetPending will never be called. Do you mean that if Receive(amsTcpHeader) throws an exception, the condition variable will also time out and be awakened by itself?

@pbruenn
Copy link
Member

pbruenn commented Nov 28, 2024

I repeat "Where exactly?".

Here we wait with a timeout:

cv.wait_until(lock, request.load()->deadline, [&]() { return !invokeId.load(); });

Only after invokeId wasn't consumed by GetPending

if (queue[portIndex].invokeId.compare_exchange_strong(currentId, 0)) {

The Wait() will return

return ADSERR_CLIENT_SYNCTIMEOUT;

Only after invokeId was consumed we would wait a second time without timeout

cv.wait(lock, [&]() { return errorCode != WAITING_FOR_RESPONSE; });

But in these cases GetPending() was successful and we either got an unknown AMS command id

response->Notify(ADSERR_CLIENT_SYNCRESINVALID);

Or we call ReceiveFrame()

ReceiveFrame<AoEResponseHeader>(response, aoeHeader.length(), aoeHeader.errorCode());

ReceiveFrame<AoEReadResponseHeader>(response, aoeHeader.length(), aoeHeader.errorCode());

And I see no codepath in ReceiveFrame without a Notify().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants