tdlib / td

Cross-platform library for building Telegram clients
https://core.telegram.org/tdlib
Boost Software License 1.0
7.11k stars 1.44k forks source link

Receive Updates could be plug up #2715

Closed FunkyYang closed 10 months ago

FunkyYang commented 10 months ago
tdlibClient, err := client.NewClient(authorizer)
if err != nil {
    log.Fatalf("NewClient error: %s", err)
}

listener := tdlibClient.GetListener()
defer listener.Close()

for update := range listener.Updates {
    if update.GetClass() == client.ClassUpdate {
        log.Printf("%#v", update)
    }
}

the code above which I use come from go-tdlib (https://github.com/zelenin/go-tdlib) and running some time,the receive channel will be plug up and I write a message in telegram manual and no response will be print in my program.I have no idea about that? can be fixed?

levlam commented 10 months ago

You need to check TDLib logs with verbosity level at least 3. Likely, the updates are sent and the issue is somewhere in the wrapper or handling of the updates.

FunkyYang commented 10 months ago

d stderr.log this is my logit seems normal

levlam commented 10 months ago

TDLib works correctly, but the log contains no calls to td_receive, which means that the app stopped to fetch updates and request responses. This may be caused by thread that call td_receive being blocked and synchronously waiting for something.

FunkyYang commented 10 months ago

so how can I fixed?

levlam commented 10 months ago

You need to find, why the app doesn't try to receive updates anymore. For example, find what blocks the thread, or forces it to terminate.

FunkyYang commented 10 months ago
func (instance *tdlib) addClient(client *Client) {
    instance.mu.Lock()
    defer instance.mu.Unlock()

    instance.clients[client.jsonClient.id] = client

    instance.once.Do(func() {
        go instance.receiver()
    })
}

func (instance *tdlib) receiver() {
    for {
        resp, err := instance.receive(instance.timeout)
        if err != nil {
            continue
        }

        client, err := instance.getClient(resp.ClientId)
        if err != nil {
            log.Print(err)
            continue
        }

        client.responses <- resp
    }
}

it could have

FunkyYang commented 10 months ago

// Receives incoming updates and request responses from the TDLib client. May be called from any thread, but
// shouldn't be called simultaneously from two different threads.
// Returned pointer will be deallocated by TDLib during next call to td_json_client_receive or td_json_client_execute
// in the same thread, so it can't be used after that.
func (instance *tdlib) receive(timeout time.Duration) (*Response, error) {
    result := C.td_receive(C.double(float64(timeout) / float64(time.Second)))
    if result == nil {
        return nil, errors.New("update receiving timeout")
    }

    data := []byte(C.GoString(result))

    var resp Response

    err := json.Unmarshal(data, &resp)
    if err != nil {
        return nil, err
    }

    resp.Data = data

    return &resp, nil
}
levlam commented 10 months ago

Do you use multiple TDLib instances simultaneously?

FunkyYang commented 10 months ago

No I write messages to many groups and collect them reply one by one

FunkyYang commented 10 months ago

use one account

levlam commented 10 months ago

Then, you need to find, why the app doesn't try to receive updates anymore. For example, find what blocks the thread processing updates, or forces it to terminate.

FunkyYang commented 10 months ago

image

levlam commented 10 months ago

If the thread is blocked by a mutex, you will see no activity in the thread.

FunkyYang commented 10 months ago

I use pprof to monitor the program and which metric should I notice?

levlam commented 10 months ago

You must check stack trace of the receiver thread to find what it does.

FunkyYang commented 10 months ago

how can I do that?

levlam commented 10 months ago

You need to use a debugger for this.

FunkyYang commented 10 months ago
func (instance *tdlib) receiver() {
    for {
        resp, err := instance.receive(instance.timeout)
        if err != nil {
            log.Print(err)
            continue
        }
        log.Printf("clientId:%d", resp.ClientId)

        client, err := instance.getClient(resp.ClientId)
        if err != nil {
            log.Print(err)
            continue
        }

        client.responses <- resp
    }
}
func (instance *tdlib) receive(timeout time.Duration) (*Response, error) {
    result := C.td_receive(C.double(float64(timeout) / float64(time.Second)))
    if result == nil {
        return nil, errors.New("update receiving timeout")
    }

    data := []byte(C.GoString(result))

    var resp Response

    err := json.Unmarshal(data, &resp)
    if err != nil {
        return nil, err
    }

    resp.Data = data

    return &resp, nil
}

image

FunkyYang commented 10 months ago
result := C.td_receive(C.double(float64(timeout) / float64(time.Second)))
    if result == nil {
        return nil, errors.New("update receiving timeout")
    }
// Receive Receives incoming updates and request responses from the TDLib client.
// You can provide string or UpdateData.
func (client *Client) Receive(timeout float64) []byte {
    result := C.td_json_client_receive(client.Client, C.double(timeout))

    return []byte(C.GoString(result))
}

difference between old and new

FunkyYang commented 10 months ago

/**
 * Receives incoming updates and request responses. Must not be called simultaneously from two different threads.
 * The returned pointer can be used until the next call to td_receive or td_execute, after which it will be deallocated by TDLib.
 * \param[in] timeout The maximum number of seconds allowed for this function to wait for new data.
 * \return JSON-serialized null-terminated incoming update or request response. May be NULL if the timeout expires.
 */
TDJSON_EXPORT const char *td_receive(double timeout);
FunkyYang commented 10 months ago

this could be a tdlib issue

FunkyYang commented 10 months ago

@levlam

FunkyYang commented 10 months ago
result := C.td_receive(C.double(float64(timeout) / float64(time.Second)))

will being blocked

FunkyYang commented 10 months ago

image

Can I use

const char *    [td_json_client_receive](https://core.telegram.org/tdlib/docs/td__json__client_8h.html#a9e0cb36bfa2bc2249905aebd7d07a4ac) (void *client, double timeout)
levlam commented 10 months ago

this could be a tdlib issue

This can't be a TDLib issue.

Can I use td_json_client_receive

The new interface with td_receive is better to use.

FunkyYang commented 10 months ago

result := C.td_receive(C.double(float64(timeout) / float64(time.Second))) will being blocked?

levlam commented 10 months ago

Yes, blocked waiting for updates and request responses from TDLib. The function isn't called.

FunkyYang commented 10 months ago

but them in a for loop which can't be break

FunkyYang commented 10 months ago

anyhing else will be blocked can make this blocking?

FunkyYang commented 10 months ago

image image the channel is fulled

FunkyYang commented 10 months ago

image I wonder why