Hi,
We use dbus in our project, and want to replace it with ubus right now, but I have some concerns about how to use ubus in multi-thread mode.
I find that ubus use a global blob_buf to send message, if I invoke ubus APIs in multi-thread mode, the data may be dirty or lost, especially invoke synchronous.
the following is my example ubus_test_multithread.c :
/*
* gcc ubus_test_multithread.c -o ubus_test_multithread -O0 -g -lubus -lubox -lpthread -I. -I./ubus -L./libubox/build -L./ubus/build -Wl,-rpath,./libubox/build -Wl,-rpath,./ubus/build
*/
#include <unistd.h>
#include <pthread.h>
#include "libubus.h"
enum {
HELLO_ID,
HELLO_MSG,
__HELLO_MAX
};
static const struct blobmsg_policy hello_multithread_policy[] = {
[HELLO_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
[HELLO_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING },
};
static int test_hello(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__HELLO_MAX];
int id = 0;
const char *msgstr = "(unknown)";
blobmsg_parse(hello_multithread_policy, ARRAY_SIZE(hello_multithread_policy), tb, blob_data(msg), blob_len(msg));
if (tb[HELLO_ID])
id = (int)blobmsg_get_u32(tb[HELLO_ID]);
if (tb[HELLO_MSG])
msgstr = blobmsg_data(tb[HELLO_MSG]);
printf("[msg]0x%x:%s\n", id, msgstr);
return 0;
}
static const struct ubus_method test_multithread_methods[] = {
UBUS_METHOD("hello", test_hello, hello_multithread_policy),
};
static struct ubus_object_type test_multithread_object_type =
UBUS_OBJECT_TYPE("test.multithread", test_multithread_methods);
static struct ubus_object test_multithread_object = {
.name = "test.multithread",
.type = &test_multithread_object_type,
.methods = test_multithread_methods,
.n_methods = ARRAY_SIZE(test_multithread_methods),
};
static void server_main(struct ubus_context *ctx)
{
int ret;
printf("%s:%d\n", __FUNCTION__, __LINE__);
ret = ubus_add_object(ctx, &test_multithread_object);
if (ret)
fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
uloop_run();
}
static uint32_t g_objID;
static void *client_thread_proc(void *data) {
char buffer[10];
struct ubus_request req;
struct blob_buf privateBlobBuf;
struct ubus_context *ctx = (struct ubus_context *)data;
uint32_t tID = pthread_self();
uint32_t objID;
int index;
int result;
printf("%s:%d tID=0x%x\n", __FUNCTION__, __LINE__, tID);
memset(&privateBlobBuf, 0, sizeof(privateBlobBuf));
if (g_objID) {
objID = g_objID;
} else {
result = ubus_lookup_id(ctx, "test.multithread", &objID);
if (result) {
fprintf(stderr, "Failed to lookup object: %s\n", ubus_strerror(result));
return NULL;
}
}
for (index = 0; index < 9; index++) {
snprintf(buffer, sizeof(buffer), "%09d", index);
blob_buf_init(&privateBlobBuf, 0);
blobmsg_add_u32(&privateBlobBuf, "id", tID);
blobmsg_add_string(&privateBlobBuf, "msg", buffer);
ubus_invoke_async(ctx, objID, "hello", privateBlobBuf.head, &req);
}
blob_buf_free(&privateBlobBuf);
return NULL;
}
static void client_main(struct ubus_context *ctx) {
pthread_t thread_clients[1];
int index;
ubus_lookup_id(ctx, "test.multithread", &g_objID);
printf("%s:%d g_objID=0x%x\n", __FUNCTION__, __LINE__, g_objID);
for (index = 0; index < sizeof(thread_clients) / sizeof(thread_clients[0]); index++) {
pthread_create(&thread_clients[index], NULL, client_thread_proc, (void *)ctx);
}
uloop_run();
}
int main(int argc, char **argv)
{
struct ubus_context *ctx;
const char *ubus_socket = NULL;
int ch;
int clientMode = 0;
while ((ch = getopt(argc, argv, "cs:")) != -1) {
switch (ch) {
case 's':
ubus_socket = optarg;
break;
case 'c':
clientMode = 1;
break;
default:
break;
}
}
argc -= optind;
argv += optind;
uloop_init();
signal(SIGPIPE, SIG_IGN);
ctx = ubus_connect(ubus_socket);
if (!ctx) {
fprintf(stderr, "Failed to connect to ubus\n");
return -1;
}
ubus_add_uloop(ctx);
if (clientMode) {
client_main(ctx);
} else {
server_main(ctx);
}
ubus_free(ctx);
uloop_done();
return 0;
}
start ubusd in backgroud first, and then
*******/ubus# ./ubus_test_multithread &
[1] 17591
*******/ubus# server_main:59
*******/ubus# ./ubus_test_multithread -c
client_main:109 g_objID=0x5653e569
client_thread_proc:78 tID=0x20dc7700
client_thread_proc:78 tID=0x205c6700
[msg]0x20dc7700:000000000
[msg]0x205c6700:000000000
[msg]0x20dc7700:000000001
[msg]0x20dc7700:000000002
[msg]0x20dc7700:000000002
[msg]0x20dc7700:000000003
[msg]0x20dc7700:000000003
[msg]0x20dc7700:000000004
[msg]0x20dc7700:000000004
[msg]0x20dc7700:000000005
[msg]0x20dc7700:000000005
[msg]0x20dc7700:000000006
[msg]0x20dc7700:000000006
[msg]0x205c6700:000000006
I create two threads in client mode, they both invoke hello@server 9 times, but the output like above, the output don't make sense.
And if I call ubus_lookup_id in each thread, all threads will hang up, because they can't get any feed back in ubus_complete_request.
So, dose it mean that I have to use thread lock when use ubus in multi-thread mode ?