Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transfer data between threads by eventfd

I wann transfer data between threads by eventfd. So I write the following codes to meet my need. My host OS is openSUSE v12.3 64bit.

 #include "stdio.h"
 #include "unistd.h"
 #include "pthread.h"
 #include "sys/eventfd.h"
 #include "sys/epoll.h"
 #define nil NULL

 int efd = -1;

 void* read_thread(void* arg)   {

        int ret = 0;
    uint64_t count = 0;
    int ep_fd = -1;
    struct epoll_event events[10];

    (void)(arg);
    if (efd < 0) {
        printf("efd not inited.\n");
        return nil;
    }

    ep_fd = epoll_create(1024);
    if (ep_fd < 0) {
        perror("epoll_create fail: ");
        return nil;
    }

    {
        struct epoll_event read_event;

        read_event.events = EPOLLIN;
        read_event.data.fd = efd;

        ret = epoll_ctl(ep_fd, EPOLL_CTL_ADD, efd, &read_event);
        if (ret < 0) {
            perror("epoll ctl failed:");
            return nil;
        }
    }

    while (1) {
        ret = epoll_wait(ep_fd, events, 10, 5000);
        if (ret > 0) {
            int i = 0;
            for (i = 0; i < ret; i++) {
                if (events[i].events & (EPOLLHUP | EPOLLERR)) {
                    printf("epoll eventfd has epoll hup.\n");
                    break;
                } else if (events[i].events & EPOLLIN) {
                    int event_fd = events[i].data.fd;
                    ret = eventfd_read(event_fd, &count);
                    if (ret < 0) {
                        perror("read fail:");
                        break;
                    } else {
                        printf("read %llu\n", count);
                    }
                }
            }
        } else if (ret == 0) {
            break;
        } else {
            perror("epoll wait error:");
            break;
        }
    }

    close(ep_fd);
    return nil;
}

int main(int argc, char *argv[]) {

    pthread_t pid = 0;
    uint64_t count = 0;
    int ret = 0;
    int i = 0;

    efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (efd < 0) {
        perror("eventfd failed.");
        return -1;
    }

    ret = pthread_create(&pid, NULL, read_thread, NULL);
    if (ret < 0) {
        perror("pthread create:");
        close(efd);
        return -2;
    }

    for (i = 0; i < 5; i++) {
        count = i + 1;
        ret = eventfd_write(efd, count);
        if (ret < 0) {
            perror("write event fd fail:");
            break;
        } else {
            printf("write %llu\n", count);
        }
    }

    sleep(3);

    pthread_join(pid, NULL);
    close(efd);
    efd = -1;

    return 0;
}

I think the behaviour of eventfd should be like a pipe. So the program's output result may be as follows:

write 1 read 1 write 2 read 2 write 3 read 3 write 4 read 4 write 5 read 5

However, its actual result is:

write 1 write 2 write 3 write 4 write 5 read 15

or

write 1 write 2 write 3 write 4 write 5 read 1 read 14

Can some nice guys tell me why the output results is the sum of input results?

I can not tranfer data correctly by eventfd between threads. I also do not like the pipe. How can I transfer data one by one correctly between threads?

like image 980
Alexander Stocks Jr Avatar asked Oct 19 '25 14:10

Alexander Stocks Jr


1 Answers

eventfd uses a counter maintained by the kernal starting with the value provided to eventfd(). this counter is incremented by the value 'sent' via write(), so if you create the eventfd with the initial value of 1 and then write() 2, the value that you read will be 3. if you then write() 5, the read() value will be 8 and so on.

eventfd is commonly used by threads within event driven patterns, but you'll need to use a queue or other container to pass an actual value, or check out signalfd() which can be used to recive data attached to signals by sigqueue().

like image 180
JSON Avatar answered Oct 21 '25 03:10

JSON