代码之家  ›  专栏  ›  技术社区  ›  kagali-san

如何避免一组值(在.so回调调用中收集)指向同一地址?

  •  0
  • kagali-san  · 技术社区  · 14 年前
    // compile with: g++ 1.cc -o test  -Wall `pkg-config --cflags --libs glib-2.0` -lgthread-2.0
    // or g++ 1.cc -o testq -lglib-2.0 -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include/ -lgthread-2.0c
    
    
    #include <glib.h>
    #include <iostream>
    #include <string.h>
    int counter;
    GAsyncQueue *q;
    
    typedef struct MyStruct {
    int foo;
    char *data;
    } *MyStructPtr;
    
    gpointer looper(gpointer data) {
            g_thread_init(NULL);
            while (1) {
                    while (g_async_queue_length(q) > 0) {
                            MyStructPtr xtransport;
                            xtransport = new struct MyStruct;
                            xtransport = (MyStructPtr)g_async_queue_try_pop(q);
    
     // The actual code is acting as weird, as if we have here, xtransport->data = last_text_value_ever_received;
                            std::cout << "str # " << xtransport->foo << ":" << xtransport->data << ":str\n";
                    }
            }
    }
    void adder(char *data) {
            char *processedData;
            // someExternalAPICallToprocessData(data, &processeddata);
            processedData = (char *)data;
            MyStructPtr dtransport;
            dtransport = new struct MyStruct;
    
            dtransport->data = processedData;//processeddata;
            dtransport->foo = ++counter;
    
            g_async_queue_push(q, (gpointer*) dtransport);
    }
    int main() {
            GThread *l;
            g_thread_init(NULL);
            q = g_async_queue_new();
            l = g_thread_create(looper, NULL, FALSE, NULL);
            sleep(2);
            char *t;
            t = strdupa("1111");
            adder(t);
            t = strdupa("222");
            adder(t);
            sleep (5);
    }
    

    这是一个更大项目的实验室模型。在项目中,会注意到一种奇怪的行为;

    而不是main(),我们坐在回调函数中,它从系统组件获取一些数据。尝试在放入队列之前打印(例如cout)这些数据,可能会得到如下结果:

    N1(回拨):aaaabbbbccc N2(回拨中):bbbbaaaacccc。

    同样,如果对(新设置的指针)dttransport->数据的访问是在创建该数据的同一代码(mainloop/callback函数)中进行的,那就没问题了。

    但无法获得队列中第一个元素的正确值!数据字段总是一样的——通过队列访问收集到的数据,只能得到上次回调时设置的值;邪恶的指针该死!

    街道1:bbbbaaaacccc。 街道2:bbbbaaaacccc。

    回调函数定义为:

    int someExternalAPICallToprocessData(...., unsigned char ** data); 
    

    它返回数据的长度,并且**-返回指向指针的指针?所以这就是解决问题。

    问题是, 会不会 someExternalAPICallTOprocessData()使用指向固定地址的指针填充&processeddata,该地址只存储上一次调用的数据 ? 如何将char*的副本返回并安全地放入MyStruct?注意,下一个函数使用偏移量。。。

    process(dequeued->data);
    void process(char *data) {
     my *ptr = NULL;
     ptr = data + SOME_SIZE_CONST
    ...
    }
    

    不知何故,memcpy'ng的processedData内容似乎分割了.so库,其中包含一些externalapicalltoprocessdata()。在模拟上下文中,它是adder()的结尾;在实际上下文中,它是内核网络的回调函数的结尾;非常有趣。

    2 回复  |  直到 14 年前
        1
  •  1
  •   jpalecek    14 年前

    是吗 一些外部apicalltoprocessdata() 将用固定的 每次呼叫的数据地址

    这有点难以解析,但如果“someexternalApi(&data)可以在每次调用时将数据设置为相同的地址”,那么可以。我不知道“someExternalApi…”是什么,但是,您必须查阅它的文档。

    你可以通过使用 strdup 在返回时 processedData ,或通过更改 MyStruct::data 给一个 std::string (这将减轻正确删除字符串的负担)。

        2
  •  0
  •   gbjbaanb    14 年前

    看起来你的推/弹出堆栈不是线程安全的。想象一下,当您将结构推送到队列中时,它首先复制foo(并增加堆栈上项目的数量),这会导致while循环从堆栈中弹出数据,并且 然后 数据被复制。哎呀。

    在这种情况下,答案是确保对堆栈的所有读写都是原子的—在对堆栈的所有调用周围粘贴一个互斥锁或关键部分。