2017-01-31 3 views
3

Я создаю простую библиотеку kdb +, которая генерирует значения в отдельном потоке и выполняет функцию обратного вызова, когда данные готовы. Приложение записывает данные в дескриптор файла в новый поток и считывает из него в цикле основного события. При попытке блокировки и разблокировки мьютекса приложение кажется segfault.Ошибка сегментации - проблема блокировки mutex pthread

Если я вводю небольшой сон в цикл, кажется, что segfault исчезает. Это предполагает, что вызов pthread_mutex_lock не блокирует поток до тех пор, пока не будет получена блокировка, как я ожидал.

#include <k.h> 
#include <pthread.h> 
#include <time.h> 

#define PIPE_CAPACITY 65536 

static int fd; 
static pthread_t thread; 
static pthread_mutex_t lock; 

K callback(int d) 
{ 
    K data; 

    // Aquire mutex lock and read from fd 
    pthread_mutex_lock(&lock); 
    read(d, &data, PIPE_CAPACITY); 
    pthread_mutex_unlock(&lock); 

    // kdb+ callback 
    k(0, (char *)"callback", r1(data), (K)0); 

    return (K)0; 
} 

void* loop() 
{ 
    while (1) { 
     struct timespec ts; 
     struct tm *time; 

     // Get seconds and nanoseconds since epoch 
     clock_gettime(CLOCK_REALTIME, &ts); 

     // Adjust for kdb+ 
     time = gmtime(&ts.tv_sec); 
     time->tm_sec = 0; 
     time->tm_min = 0; 
     time->tm_hour = 0; 
     ts.tv_sec -= mktime(time); // Subtract seconds between epoch and midnight 

     // Create kdb+ timestamp 
     K data = ktj(-KN, ts.tv_sec * 1000000000 + ts.tv_nsec); 

     // Aquire mutex lock and write to fd 
     pthread_mutex_lock(&lock); 
     write(fd, &data, sizeof(K)); 
     pthread_mutex_unlock(&lock); 
    } 
} 

K init() 
{ 
    // Initialize mutex 
    pthread_mutex_init(&lock, NULL); 

    // Create file descriptor 
    fd = eventfd(0, 0); 

    // Register callback 
    sd1(fd, callback); 

    // Launch thread 
    pthread_create(&thread, NULL, loop, NULL); 
} 
+0

Не связано, но вам действительно следует избегать однобуквенных типов имен, таких как 'K'. –

+0

Можете ли вы предоставить «основную» функцию? Инициализация вступает в силу до создания потоков? – Emisilve86

+0

@MichaelWalz Это относится к API, с которым мне нужно работать, а не к моему личному выбору - содержится в [kh] (http://code.kx.com/wsvn/code/kx/kdb%2B/c/c/ kh) –

ответ

3

Напомним, что K является тип указателя определен в кк как:

typedef struct k0{..}*K; 

Это означает, что вы отправляете указатель на объект, созданный в «петле» нить на обратный вызов, выполненный в основной нить. Это не работает, потому что kdb + использует отдельное выделение памяти для каждого потока. Вместо этого я бы рекомендовал передать копию данных.

Другая проблема заключается в линии

read(d, &data, PIPE_CAPACITY); 

Вы читаете 65536 байт, но передать адрес переменной 8 байт в качестве пункта назначения. Причина, по которой вы не получаете segfault, когда вводите задержку, заключается в том, что в этом случае цикл не получает возможность писать более 8 байтов между чтениями.

Наконец, я не уверен, что вы можете использовать дескриптор файла, возвращаемый eventfd, в качестве буфера чтения-записи. Я бы рекомендовал использовать добрый старый вызов pipe().

Следующая модификация кода работает для меня:

#include <k.h> 
#include <pthread.h> 
#include <time.h> 
#include <unistd.h> 

static int fd[2]; 
static pthread_t thread; 
static pthread_mutex_t lock; 

K callback(int d) 
{ 
    K data = ktj(-KN, 0); 

    // Aquire mutex lock and read from fd 
    pthread_mutex_lock(&lock); 
    read(d, (void *)&data->j, sizeof(data->j)); 
    pthread_mutex_unlock(&lock); 

    // kdb+ callback 
    k(0, (char *)"callback", data, (K)0); 

    return (K)0; 
} 

void* loop() 
{ 
    while (1) { 
     struct timespec ts; 
     struct tm *time; 

     // Get seconds and nanoseconds since epoch 
     clock_gettime(CLOCK_REALTIME, &ts); 

     // Adjust for kdb+ 
     time = gmtime(&ts.tv_sec); 
     time->tm_sec = 0; 
     time->tm_min = 0; 
     time->tm_hour = 0; 
     ts.tv_sec -= mktime(time); // Subtract seconds between epoch and midnight 

     // Create kdb+ timestamp 
     J data = (J)ts.tv_sec * 1000000000 + ts.tv_nsec; 

     // Aquire mutex lock and write to fd 
     pthread_mutex_lock(&lock); 
     write(fd[1], &data, sizeof(data)); 
     pthread_mutex_unlock(&lock); 
    } 
} 

K1(init) 
{ 
    // Initialize mutex 
    pthread_mutex_init(&lock, NULL); 

    // Create file descriptor 
    pipe(fd); 

    // Register callback 
    sd1(fd[0], callback); 

    // Launch thread 
    pthread_create(&thread, NULL, loop, NULL); 

    R ktj(0, 0); 
} 

Чтобы проверить, скопировать код, указанный выше в хс, компилировать

$ gcc -Wall -shared -fPIC -I $(pwd) -DKXVER=3 x.c -o x.so 

и запустить следующий Q-код:

callback:0N! 
init:`:./x 2:(`init;1) 
init[] 
Смежные вопросы