C 从读卡器进行只读访问的循环缓冲区

C 从读卡器进行只读访问的循环缓冲区,c,circular-buffer,C,Circular Buffer,我想在读卡器只有只读访问权限的情况下构建一个循环缓冲区,但它有一个问题。为了实现平滑的滚动,我让编写器将滚动数据结构的迭代器+1中的id设置为0,并向读取器签入。在第一次滚动之前,我的算法似乎运行良好,然后出于某种原因,resder将从编写器显然设置的id中读取0。 我这里有一些可编译的示例代码来演示这个问题: #include <stdio.h> #include <time.h> #include <stdlib.h> #include <strin

我想在读卡器只有只读访问权限的情况下构建一个循环缓冲区,但它有一个问题。为了实现平滑的滚动,我让编写器将滚动数据结构的迭代器+1中的id设置为0,并向读取器签入。在第一次滚动之前,我的算法似乎运行良好,然后出于某种原因,resder将从编写器显然设置的id中读取0。 我这里有一些可编译的示例代码来演示这个问题:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#define NUM_ALM 5
#define ERROR   -1
#define OK      0

//even IDs = alarm active
//odd IDs  = alarm clear
enum alarmid {
    BFD_ACT     = 0x02,
    BFD_CLR     = 0x03,
    LOS_ACT     = 0x0C
};
typedef struct alarm_s {
    long timestamp;
    int alarmid;
    int arg1;
    int arg2;
}alarm_t;
int alarm_add(int id, int arg1, int arg2);
int next_alarm_read(alarm_t *res);
void *alarm_reader(void *arg);

static alarm_t *roller;
pthread_cond_t cv;
pthread_mutex_t mutex;
int main (void)
{
    int i =0;
    alarm_t dat;
    pthread_t reader;
    int ret;

    roller = calloc(NUM_ALM,sizeof(alarm_t));
    printf("allocated memory: %lukB\n",(sizeof(alarm_t)*NUM_ALM)/1024);

    for (i = 1; i< NUM_ALM; i++){
        alarm_add(LOS_ACT,i,0);
    }
    ret = pthread_create(&reader,NULL,alarm_reader,NULL);
    if (ret){
        printf("Error - pthread_create() return code: %d\n",ret);
        return ERROR;
    }
    sleep(1);
    alarm_add(BFD_ACT,8,0);
    alarm_add(BFD_ACT,8,0);
    alarm_add(BFD_ACT,8,0);
    alarm_add(BFD_ACT,8,0);
    alarm_add(BFD_CLR,8,0);
    alarm_add(BFD_CLR,8,0);
    alarm_add(BFD_CLR,8,0);
    alarm_add(BFD_CLR,8,0);
    alarm_add(BFD_ACT,8,0);

    pthread_join(reader,NULL);
}

void *alarm_reader(void *arg)
{
    static alarm_t dat={0};
    int err = 0;
    while(err <= 2)
    {
        if (next_alarm_read(&dat)== OK)
            printf("read alarm id %d, arg1 %d,arg2 %d\n",dat.alarmid,dat.arg1,dat.arg2);
        else{
            printf("alarm_reader() next_alarm_read() returned ERROR, wait\n");
            pthread_mutex_lock(&mutex);
            pthread_cond_wait(&cv, &mutex);
            pthread_mutex_unlock(&mutex);

            err++;
        }
    }
    printf("alarm_reader exit!\n");
}
int alarm_add(int id, int arg1, int arg2)
{
    static int i = 0;
    alarm_t dat={0};
    if (i<NUM_ALM){
        dat.timestamp = time(NULL);
        dat.alarmid = id;
        dat.arg1 = arg1;
        dat.arg2 = arg2;

        if (&roller[i]){
            memcpy(&roller[i],&dat,sizeof(alarm_t));
            if (i+1<NUM_ALM)
                roller[i+1].alarmid = 0;
            else
                roller[0].alarmid = 0;
            pthread_cond_signal(&cv);
            printf("added id %d, arg1 %d, arg2 %d @%d\n",roller[i].alarmid,roller[i].arg1,roller[i].arg2,i);
            i++;
        }
    } else {
        i = 0;
    }
    return 0;
}

int next_alarm_read(alarm_t *res)
{
    static int i = 0;
    static long prev_time = 0;
    if (!res)
        return ERROR;

    if (i<NUM_ALM)
    {
        if (roller[i].alarmid!=0){
            printf("next_alarm_read() reading @%d\n",i);
            res->timestamp = roller[i].timestamp;
            res->alarmid = roller[i].alarmid;
            res->arg1 = roller[i].arg1;
            res->arg2 = roller[i].arg2;
            prev_time = roller[i].timestamp;
            i++;
        } else {
            printf("next_alarm_read() @%d is %d,return ERROR\n",i,roller[i].alarmid);

            return ERROR;
        }
    } else {
        i = 0;
    }
    return OK;
}
next\u alarm\u read()@1的底部打印为0,返回错误
错误,id应为2。我想知道为什么这样做不起作用?

有几个问题:

我不确定
如果(&roller[I])
应该做什么/意味着什么

main
中的
sleep
实际上并不需要,我怀疑这是为了改善下面的其他问题

报警\u添加
将在滚动点删除一个条目

此外,它可能会使读卡器溢出,并在读卡器看到条目之前覆盖条目(即竞争条件)

读取器和写入器都需要相互查看当前队列索引(即,它们不应该是函数范围的
静态
),以防止溢出/竞争

应该有两个条件变量,而不仅仅是一个:

  • 写入程序检测到队列已满,需要阻塞,直到读取器清空条目
  • 读卡器检测到一个空队列,需要阻塞,直到写入器添加一个新条目

  • 下面是代码的重构版本,可以解决这些问题。我添加了一些调试代码。它可能并不完美[而且可能在保守主义方面出错],但它应该让你走得更远[请原谅这种无缘无故的风格清理]:

    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    #include <unistd.h>
    
    #define NUM_ALM 5
    #define ERROR   -1
    #define OK      0
    
    double tvzero;
    
    //even IDs = alarm active
    //odd IDs  = alarm clear
    enum alarmid {
        BFD_ACT = 0x02,
        BFD_CLR = 0x03,
        LOS_ACT = 0x0C
    };
    
    typedef struct alarm_s {
        long timestamp;
        int alarmid;
        int arg1;
        int arg2;
    } alarm_t;
    
    void alarm_add(int id, int arg1, int arg2);
    int next_alarm_read(alarm_t * res);
    void *alarm_reader(void *arg);
    
    static alarm_t *roller;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    // reader variables
    pthread_cond_t cv_notempty;             // writer signals when queue not empty
    volatile int need_notempty;             // reader sets this before waiting
    volatile int idxdeq;                    // reader's queue index
    
    // writer variables
    pthread_cond_t cv_notfull;              // reader signals when queue not full
    volatile int need_notfull;              // writer sets this before waiting
    volatile int idxenq;                    // writer's queue index
    
    volatile int stopall;
    
    double
    tvgetf(void)
    {
        struct timespec ts;
        double sec;
    
        clock_gettime(CLOCK_REALTIME,&ts);
    
        sec = ts.tv_nsec;
        sec /= 1e9;
        sec += ts.tv_sec;
    
        sec -= tvzero;
    
        return sec;
    }
    
    #define DBG(_reason) \
        dbg(_reason)
    
    void
    dbg(const char *reason)
    {
        double tvnow;
    
        tvnow = tvgetf();
        printf("[%.9f] %s\n",tvnow,reason);
    }
    
    int
    main(void)
    {
        int i = 0;
        pthread_t reader;
        int ret;
    
        tvzero = tvgetf();
    
        roller = calloc(NUM_ALM, sizeof(alarm_t));
        printf("allocated memory: %lukB\n", (sizeof(alarm_t) * NUM_ALM) / 1024);
    
        // NOTE: queuing more than a full queue here will cause writer to block
        // forever because reader is not yet started
        for (i = 1; i < NUM_ALM; i++) {
            alarm_add(LOS_ACT, i, 0);
        }
    
        ret = pthread_create(&reader, NULL, alarm_reader, NULL);
        if (ret) {
            printf("Error - pthread_create() return code: %d\n", ret);
            return ERROR;
        }
    
    #if 0
        sleep(1);
    #endif
    
        alarm_add(BFD_ACT, 8, 0);
        alarm_add(BFD_ACT, 8, 0);
        alarm_add(BFD_ACT, 8, 0);
        alarm_add(BFD_ACT, 8, 0);
        alarm_add(BFD_CLR, 8, 0);
        alarm_add(BFD_CLR, 8, 0);
        alarm_add(BFD_CLR, 8, 0);
        alarm_add(BFD_CLR, 8, 0);
        alarm_add(BFD_ACT, 8, 0);
    
        // tell reader that all items are queued and it should stop when it
        // processes the final item
        pthread_mutex_lock(&mutex);
        stopall = 1;
        if (need_notempty)
            pthread_cond_signal(&cv_notempty);
        pthread_mutex_unlock(&mutex);
    
        pthread_join(reader, NULL);
    
        return 0;
    }
    
    // RETURNS: queue index to process (-1=empty)
    int
    queue_notempty(void)
    {
        int curidx;
    
        do {
            curidx = idxdeq;
    
            // queue is empty
            if (curidx == idxenq) {
                curidx = -1;
                break;
            }
    
            // advance dequeue index
            idxdeq += 1;
            idxdeq %= NUM_ALM;
        } while (0);
    
        return curidx;
    }
    
    // RETURNS: queue index to use (-1=full)
    int
    queue_notfull(void)
    {
        int nxtidx;
        int curidx;
    
        do {
            // get current index
            curidx = idxenq;
    
            // advance to next slot (wrapping if necessary)
            nxtidx = curidx;
            nxtidx += 1;
            nxtidx %= NUM_ALM;
    
            // queue is full
            if (nxtidx == idxdeq) {
                curidx = -1;
                break;
            }
    
            // store back adjusted index
            idxenq = nxtidx;
        } while (0);
    
        return curidx;
    }
    
    void *
    alarm_reader(void *arg)
    {
        alarm_t dat = { 0 };
    
        while (1) {
            if (next_alarm_read(&dat))
                break;
            printf("read alarm id %d, arg1 %d,arg2 %d\n",
                dat.alarmid, dat.arg1, dat.arg2);
        }
    
        printf("alarm_reader exit!\n");
    
        return (void *) 0;
    }
    
    void
    alarm_add(int id, int arg1, int arg2)
    {
        int curidx;
        alarm_t *rol;
    
        pthread_mutex_lock(&mutex);
    
        while (1) {
            curidx = queue_notfull();
    
            // have an open slot -- store item into it
            if (curidx >= 0) {
                rol = &roller[curidx];
    
                rol->timestamp = time(NULL);
                rol->alarmid = id;
                rol->arg1 = arg1;
                rol->arg2 = arg2;
    
                printf("added id %d, arg1 %d, arg2 %d @%d\n",
                    rol->alarmid, rol->arg1, rol->arg2, curidx);
    
                // unblock reader if necessary
                if (need_notempty) {
                    DBG("writer signal notempty");
                    need_notempty = 0;
                    pthread_cond_signal(&cv_notempty);
                }
    
                break;
            }
    
            // queue is full -- wait for reader to free up some space
            DBG("writer need_notfull");
            need_notfull = 1;
            pthread_cond_wait(&cv_notfull,&mutex);
            DBG("writer wakeup");
        }
    
        pthread_mutex_unlock(&mutex);
    }
    
    // RETURNS: 1=stop, 0=normal
    int
    next_alarm_read(alarm_t *res)
    {
        //static long prev_time = 0;
        int curidx;
        alarm_t *rol;
        int stopflg = 0;
    
        pthread_mutex_lock(&mutex);
    
        while (1) {
            curidx = queue_notempty();
    
            // queue has an entry -- process it
            if (curidx >= 0) {
                rol = &roller[curidx];
    
                printf("next_alarm_read() reading @%d\n", curidx);
                *res = *rol;
                //prev_time = rol->timestamp;
    
                // if writer is waiting/blocking, wake it up because we just
                // freed up a queue slot
                if (need_notfull) {
                    DBG("reader signal notfull");
                    need_notfull = 0;
                    pthread_cond_signal(&cv_notfull);
                }
    
                break;
            }
    
            // stop when master has enqueued everything
            stopflg = stopall;
            if (stopflg)
                break;
    
            // queue is empty -- we must wait for writer to add something
            DBG("reader need_notempty");
            need_notempty = 1;
            pthread_cond_wait(&cv_notempty,&mutex);
        }
    
        pthread_mutex_unlock(&mutex);
    
        return stopflg;
    }
    
    读者应检查输入的
    arg1
    值。它们应如上所述增加。如果没有,则存在逻辑错误或竞争条件


    这是我的代码的更新版本,带有诊断/单元测试模式的
    -D
    选项。请注意,所有打印均已禁用,以允许其以极高速度运行:

    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    #include <unistd.h>
    
    #define NUM_ALM 5
    #define ERROR   -1
    #define OK      0
    
    int opt_diag;
    double tvzero;
    
    //even IDs = alarm active
    //odd IDs  = alarm clear
    enum alarmid {
        BFD_ACT = 0x02,
        BFD_CLR = 0x03,
        LOS_ACT = 0x0C
    };
    
    typedef struct alarm_s {
        long timestamp;
        int alarmid;
        int arg1;
        int arg2;
    } alarm_t;
    
    void alarm_add(int id, int arg1, int arg2);
    int next_alarm_read(alarm_t * res);
    void *alarm_reader(void *arg);
    
    static alarm_t *roller;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    // reader variables
    pthread_cond_t cv_notempty;             // writer signals when queue not empty
    volatile int need_notempty;             // reader sets this before waiting
    volatile int idxdeq;                    // reader's queue index
    
    // writer variables
    pthread_cond_t cv_notfull;              // reader signals when queue not full
    volatile int need_notfull;              // writer sets this before waiting
    volatile int idxenq;                    // writer's queue index
    
    volatile int stopall;
    
    double
    tvgetf(void)
    {
        struct timespec ts;
        double sec;
    
        clock_gettime(CLOCK_REALTIME,&ts);
    
        sec = ts.tv_nsec;
        sec /= 1e9;
        sec += ts.tv_sec;
    
        sec -= tvzero;
    
        return sec;
    }
    
    #define prtf(_fmt...) \
        do { \
            if (opt_diag) \
                break; \
            printf(_fmt); \
        } while (0)
    
    #define DBG(_reason) \
        dbg(_reason)
    
    void
    dbg(const char *reason)
    {
        double tvnow;
    
        if (! opt_diag) {
            tvnow = tvgetf();
            printf("[%.9f] %s\n",tvnow,reason);
        }
    }
    
    int
    main(int argc,char **argv)
    {
        int i = 0;
        char *cp;
        pthread_t reader;
        int ret;
    
        --argc;
        ++argv;
    
        for (;  argc > 0;  --argc, ++argv) {
            cp = *argv;
            if (*cp != '-')
                break;
    
            switch (cp[1]) {
            case 'D':
                cp += 2;
                opt_diag = (*cp != 0) ? atoi(cp) : 10000000;
                break;
            }
        }
    
        tvzero = tvgetf();
    
        roller = calloc(NUM_ALM, sizeof(alarm_t));
        printf("allocated memory: %lukB\n", (sizeof(alarm_t) * NUM_ALM) / 1024);
    
        // NOTE: queuing more than a full queue here will cause writer to block
        // forever because reader is not yet started
        if (! opt_diag) {
            for (i = 1; i < NUM_ALM; i++) {
                alarm_add(LOS_ACT, i, 0);
            }
        }
    
        ret = pthread_create(&reader, NULL, alarm_reader, NULL);
        if (ret) {
            printf("Error - pthread_create() return code: %d\n", ret);
            return ERROR;
        }
    
    #if 0
        sleep(1);
    #endif
    
        if (opt_diag) {
            for (i = 1; i < opt_diag; i++) {
                alarm_add(LOS_ACT, i, 0);
            }
        }
        else {
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
        }
    
        // tell reader that all items are queued and it should stop when it
        // processes the final item
        pthread_mutex_lock(&mutex);
        stopall = 1;
        if (need_notempty)
            pthread_cond_signal(&cv_notempty);
        pthread_mutex_unlock(&mutex);
    
        pthread_join(reader, NULL);
    
        return 0;
    }
    
    // RETURNS: queue index to process (-1=empty)
    int
    queue_notempty(void)
    {
        int curidx;
    
        do {
            curidx = idxdeq;
    
            // queue is empty
            if (curidx == idxenq) {
                curidx = -1;
                break;
            }
    
            // advance dequeue index
            idxdeq += 1;
            idxdeq %= NUM_ALM;
        } while (0);
    
        return curidx;
    }
    
    // RETURNS: queue index to use (-1=full)
    int
    queue_notfull(void)
    {
        int nxtidx;
        int curidx;
    
        do {
            // get current index
            curidx = idxenq;
    
            // advance to next slot (wrapping if necessary)
            nxtidx = curidx;
            nxtidx += 1;
            nxtidx %= NUM_ALM;
    
            // queue is full
            if (nxtidx == idxdeq) {
                curidx = -1;
                break;
            }
    
            // store back adjusted index
            idxenq = nxtidx;
        } while (0);
    
        return curidx;
    }
    
    void *
    alarm_reader(void *arg)
    {
        alarm_t dat = { 0 };
        static int expval = 1;
    
        while (1) {
            if (next_alarm_read(&dat))
                break;
    
            if (opt_diag) {
                if (dat.arg1 != expval) {
                    printf("expected: %d got %d\n",expval,dat.arg1);
                    exit(1);
                }
                ++expval;
            }
    
            prtf("read alarm id %d, arg1 %d,arg2 %d\n",
                dat.alarmid, dat.arg1, dat.arg2);
        }
    
        printf("alarm_reader exit!\n");
    
        return (void *) 0;
    }
    
    void
    alarm_add(int id, int arg1, int arg2)
    {
        int curidx;
        alarm_t *rol;
    
        pthread_mutex_lock(&mutex);
    
        while (1) {
            curidx = queue_notfull();
    
            // have an open slot -- store item into it
            if (curidx >= 0) {
                rol = &roller[curidx];
    
                rol->timestamp = time(NULL);
                rol->alarmid = id;
                rol->arg1 = arg1;
                rol->arg2 = arg2;
    
                prtf("added id %d, arg1 %d, arg2 %d @%d\n",
                    rol->alarmid, rol->arg1, rol->arg2, curidx);
    
                // unblock reader if necessary
                if (need_notempty) {
                    DBG("writer signal notempty");
                    need_notempty = 0;
                    pthread_cond_signal(&cv_notempty);
                }
    
                break;
            }
    
            // queue is full -- wait for reader to free up some space
            DBG("writer need_notfull");
            need_notfull = 1;
            pthread_cond_wait(&cv_notfull,&mutex);
            DBG("writer wakeup");
        }
    
        pthread_mutex_unlock(&mutex);
    }
    
    // RETURNS: 1=stop, 0=normal
    int
    next_alarm_read(alarm_t *res)
    {
        //static long prev_time = 0;
        int curidx;
        alarm_t *rol;
        int stopflg = 0;
    
        pthread_mutex_lock(&mutex);
    
        while (1) {
            curidx = queue_notempty();
    
            // queue has an entry -- process it
            if (curidx >= 0) {
                rol = &roller[curidx];
    
                prtf("next_alarm_read() reading @%d\n", curidx);
                *res = *rol;
                //prev_time = rol->timestamp;
    
                // if writer is waiting/blocking, wake it up because we just
                // freed up a queue slot
                if (need_notfull) {
                    DBG("reader signal notfull");
                    need_notfull = 0;
                    pthread_cond_signal(&cv_notfull);
                }
    
                break;
            }
    
            // stop when master has enqueued everything
            stopflg = stopall;
            if (stopflg)
                break;
    
            // queue is empty -- we must wait for writer to add something
            DBG("reader need_notempty");
            need_notempty = 1;
            pthread_cond_wait(&cv_notempty,&mutex);
        }
    
        pthread_mutex_unlock(&mutex);
    
        return stopflg;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #定义NUM_ALM 5
    #定义错误-1
    #定义OK 0
    int opt_diag;
    双tvzero;
    //偶数IDs=报警激活
    //奇数ID=警报清除
    枚举报警ID{
    BFD_ACT=0x02,
    BFD_CLR=0x03,
    LOS_ACT=0x0C
    };
    类型定义结构报警{
    长时间戳;
    内部报警ID;
    int arg1;
    int-arg2;
    }警报;
    无效报警添加(int id、int arg1、int arg2);
    int next_alarm_read(报警恢复);
    无效*报警读取器(无效*参数);
    静态报警滚轮;
    pthread\u mutex\u t mutex=pthread\u mutex\u初始值设定项;
    //读取器变量
    pthread_cond_t cv_notempty;//写入程序在队列不为空时发出信号
    volatile int需要\u notempty;//读卡器在等待之前设置此选项
    易失性int-idxdeq;//读者队列索引
    //写入变量
    pthread_cond_t cv_notfull;//读卡器在队列未满时发出信号
    volatile int需要_notfull;//作者在等待之前设置此选项
    易失性int-idxenq;//写入者队列索引
    易变int-stopall;
    双重的
    tvgetf(无效)
    {
    结构timespects;
    双秒;
    时钟获取时间(时钟实时,&ts);
    sec=ts.tv\u nsec;
    sec/=1e9;
    秒+=ts.tv_秒;
    sec-=tvzero;
    返回秒;
    }
    #定义prtf(_fmt…)\
    做{\
    如果(选择诊断)\
    中断\
    printf(_fmt)\
    }而(0)
    #定义DBG(_原因)\
    dbg(_原因)
    无效的
    dbg(常量字符*原因)
    {
    双tvnow;
    如果(!opt_diag){
    tvnow=tvgetf();
    printf(“[%.9f]%s\n”,tvnow,原因);
    }
    }
    int
    主(内部argc,字符**argv)
    {
    int i=0;
    char*cp;
    pthread_t读取器;
    int ret;
    --argc;
    ++argv;
    对于(;argc>0;--argc,++argv){
    cp=*argv;
    如果(*cp!='-'))
    打破
    交换机(cp[1]){
    案例“D”:
    cp+=2;
    opt_diag=(*cp!=0)?atoi(cp):10000000;
    打破
    }
    }
    tvzero=tvgetf();
    滚筒=calloc(数量ALM,大小F(报警));
    printf(“已分配内存:%lukB\n”,(sizeof(报警)*NUM\u ALM)/1024);
    //注意:在此处排队超过一个完整队列将导致写入程序阻塞
    //永远,因为读者还没有开始
    如果(!opt_diag){
    对于(i=1;i    for (i = 1; i < 10000000; i++) {
            alarm_add(LOS_ACT, i, 0);
        }
    
    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    #include <unistd.h>
    
    #define NUM_ALM 5
    #define ERROR   -1
    #define OK      0
    
    int opt_diag;
    double tvzero;
    
    //even IDs = alarm active
    //odd IDs  = alarm clear
    enum alarmid {
        BFD_ACT = 0x02,
        BFD_CLR = 0x03,
        LOS_ACT = 0x0C
    };
    
    typedef struct alarm_s {
        long timestamp;
        int alarmid;
        int arg1;
        int arg2;
    } alarm_t;
    
    void alarm_add(int id, int arg1, int arg2);
    int next_alarm_read(alarm_t * res);
    void *alarm_reader(void *arg);
    
    static alarm_t *roller;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    // reader variables
    pthread_cond_t cv_notempty;             // writer signals when queue not empty
    volatile int need_notempty;             // reader sets this before waiting
    volatile int idxdeq;                    // reader's queue index
    
    // writer variables
    pthread_cond_t cv_notfull;              // reader signals when queue not full
    volatile int need_notfull;              // writer sets this before waiting
    volatile int idxenq;                    // writer's queue index
    
    volatile int stopall;
    
    double
    tvgetf(void)
    {
        struct timespec ts;
        double sec;
    
        clock_gettime(CLOCK_REALTIME,&ts);
    
        sec = ts.tv_nsec;
        sec /= 1e9;
        sec += ts.tv_sec;
    
        sec -= tvzero;
    
        return sec;
    }
    
    #define prtf(_fmt...) \
        do { \
            if (opt_diag) \
                break; \
            printf(_fmt); \
        } while (0)
    
    #define DBG(_reason) \
        dbg(_reason)
    
    void
    dbg(const char *reason)
    {
        double tvnow;
    
        if (! opt_diag) {
            tvnow = tvgetf();
            printf("[%.9f] %s\n",tvnow,reason);
        }
    }
    
    int
    main(int argc,char **argv)
    {
        int i = 0;
        char *cp;
        pthread_t reader;
        int ret;
    
        --argc;
        ++argv;
    
        for (;  argc > 0;  --argc, ++argv) {
            cp = *argv;
            if (*cp != '-')
                break;
    
            switch (cp[1]) {
            case 'D':
                cp += 2;
                opt_diag = (*cp != 0) ? atoi(cp) : 10000000;
                break;
            }
        }
    
        tvzero = tvgetf();
    
        roller = calloc(NUM_ALM, sizeof(alarm_t));
        printf("allocated memory: %lukB\n", (sizeof(alarm_t) * NUM_ALM) / 1024);
    
        // NOTE: queuing more than a full queue here will cause writer to block
        // forever because reader is not yet started
        if (! opt_diag) {
            for (i = 1; i < NUM_ALM; i++) {
                alarm_add(LOS_ACT, i, 0);
            }
        }
    
        ret = pthread_create(&reader, NULL, alarm_reader, NULL);
        if (ret) {
            printf("Error - pthread_create() return code: %d\n", ret);
            return ERROR;
        }
    
    #if 0
        sleep(1);
    #endif
    
        if (opt_diag) {
            for (i = 1; i < opt_diag; i++) {
                alarm_add(LOS_ACT, i, 0);
            }
        }
        else {
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
        }
    
        // tell reader that all items are queued and it should stop when it
        // processes the final item
        pthread_mutex_lock(&mutex);
        stopall = 1;
        if (need_notempty)
            pthread_cond_signal(&cv_notempty);
        pthread_mutex_unlock(&mutex);
    
        pthread_join(reader, NULL);
    
        return 0;
    }
    
    // RETURNS: queue index to process (-1=empty)
    int
    queue_notempty(void)
    {
        int curidx;
    
        do {
            curidx = idxdeq;
    
            // queue is empty
            if (curidx == idxenq) {
                curidx = -1;
                break;
            }
    
            // advance dequeue index
            idxdeq += 1;
            idxdeq %= NUM_ALM;
        } while (0);
    
        return curidx;
    }
    
    // RETURNS: queue index to use (-1=full)
    int
    queue_notfull(void)
    {
        int nxtidx;
        int curidx;
    
        do {
            // get current index
            curidx = idxenq;
    
            // advance to next slot (wrapping if necessary)
            nxtidx = curidx;
            nxtidx += 1;
            nxtidx %= NUM_ALM;
    
            // queue is full
            if (nxtidx == idxdeq) {
                curidx = -1;
                break;
            }
    
            // store back adjusted index
            idxenq = nxtidx;
        } while (0);
    
        return curidx;
    }
    
    void *
    alarm_reader(void *arg)
    {
        alarm_t dat = { 0 };
        static int expval = 1;
    
        while (1) {
            if (next_alarm_read(&dat))
                break;
    
            if (opt_diag) {
                if (dat.arg1 != expval) {
                    printf("expected: %d got %d\n",expval,dat.arg1);
                    exit(1);
                }
                ++expval;
            }
    
            prtf("read alarm id %d, arg1 %d,arg2 %d\n",
                dat.alarmid, dat.arg1, dat.arg2);
        }
    
        printf("alarm_reader exit!\n");
    
        return (void *) 0;
    }
    
    void
    alarm_add(int id, int arg1, int arg2)
    {
        int curidx;
        alarm_t *rol;
    
        pthread_mutex_lock(&mutex);
    
        while (1) {
            curidx = queue_notfull();
    
            // have an open slot -- store item into it
            if (curidx >= 0) {
                rol = &roller[curidx];
    
                rol->timestamp = time(NULL);
                rol->alarmid = id;
                rol->arg1 = arg1;
                rol->arg2 = arg2;
    
                prtf("added id %d, arg1 %d, arg2 %d @%d\n",
                    rol->alarmid, rol->arg1, rol->arg2, curidx);
    
                // unblock reader if necessary
                if (need_notempty) {
                    DBG("writer signal notempty");
                    need_notempty = 0;
                    pthread_cond_signal(&cv_notempty);
                }
    
                break;
            }
    
            // queue is full -- wait for reader to free up some space
            DBG("writer need_notfull");
            need_notfull = 1;
            pthread_cond_wait(&cv_notfull,&mutex);
            DBG("writer wakeup");
        }
    
        pthread_mutex_unlock(&mutex);
    }
    
    // RETURNS: 1=stop, 0=normal
    int
    next_alarm_read(alarm_t *res)
    {
        //static long prev_time = 0;
        int curidx;
        alarm_t *rol;
        int stopflg = 0;
    
        pthread_mutex_lock(&mutex);
    
        while (1) {
            curidx = queue_notempty();
    
            // queue has an entry -- process it
            if (curidx >= 0) {
                rol = &roller[curidx];
    
                prtf("next_alarm_read() reading @%d\n", curidx);
                *res = *rol;
                //prev_time = rol->timestamp;
    
                // if writer is waiting/blocking, wake it up because we just
                // freed up a queue slot
                if (need_notfull) {
                    DBG("reader signal notfull");
                    need_notfull = 0;
                    pthread_cond_signal(&cv_notfull);
                }
    
                break;
            }
    
            // stop when master has enqueued everything
            stopflg = stopall;
            if (stopflg)
                break;
    
            // queue is empty -- we must wait for writer to add something
            DBG("reader need_notempty");
            need_notempty = 1;
            pthread_cond_wait(&cv_notempty,&mutex);
        }
    
        pthread_mutex_unlock(&mutex);
    
        return stopflg;
    }
    
    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    #include <unistd.h>
    
    int opt_diag;
    
    #define NUM_ALM 5
    #define ERROR   -1
    #define OK      0
    
    //even IDs = alarm active
    //odd IDs  = alarm clear
    enum alarmid {
        BFD_ACT = 0x02,
        BFD_CLR = 0x03,
        LOS_ACT = 0x0C
    };
    typedef struct alarm_s {
        long timestamp;
        int alarmid;
        int arg1;
        int arg2;
    } alarm_t;
    int alarm_add(int id, int arg1, int arg2);
    int next_alarm_read(alarm_t * res);
    void *alarm_reader(void *arg);
    
    static alarm_t *roller;
    pthread_cond_t cv;
    pthread_mutex_t mutex;
    
    #define prtf(_fmt...) \
        do { \
            if (opt_diag) \
                break; \
            printf(_fmt); \
        } while (0)
    
    int
    main(int argc,char **argv)
    {
        int i = 0;
        char *cp;
        pthread_t reader;
        int ret;
    
        --argc;
        ++argv;
    
        for (;  argc > 0;  --argc, ++argv) {
            cp = *argv;
            if (*cp != '-')
                break;
    
            switch (cp[1]) {
            case 'D':
                cp += 2;
                opt_diag = (*cp != 0) ? atoi(cp) : 10000000;
                break;
            }
        }
    
        roller = calloc(NUM_ALM, sizeof(alarm_t));
        printf("allocated memory: %lukB\n", (sizeof(alarm_t) * NUM_ALM) / 1024);
    
        if (! opt_diag) {
            for (i = 1; i < NUM_ALM; i++) {
                alarm_add(LOS_ACT, i, 0);
            }
        }
    
        ret = pthread_create(&reader, NULL, alarm_reader, NULL);
        if (ret) {
            printf("Error - pthread_create() return code: %d\n", ret);
            return ERROR;
        }
    
        if (opt_diag) {
            for (i = 1; i < opt_diag; i++) {
                alarm_add(LOS_ACT, i, 0);
            }
        }
        else {
            sleep(1);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_CLR, 8, 0);
            alarm_add(BFD_ACT, 8, 0);
        }
    
        pthread_join(reader, NULL);
    }
    
    void *
    alarm_reader(void *arg)
    {
        static alarm_t dat = { 0 };
        int expval = 1;
        int err = 0;
    
        while (err <= 2) {
            if (next_alarm_read(&dat) == OK) {
                prtf("read alarm id %d, arg1 %d,arg2 %d\n", dat.alarmid, dat.arg1, dat.arg2);
                if (opt_diag) {
                    if (dat.arg1 != expval) {
                        printf("expected: %d got %d\n",expval,dat.arg1);
                        exit(1);
                    }
                    ++expval;
                }
            }
            else {
                prtf("alarm_reader() next_alarm_read() returned ERROR, wait\n");
                pthread_mutex_lock(&mutex);
                pthread_cond_wait(&cv, &mutex);
                pthread_mutex_unlock(&mutex);
    
                err++;
            }
        }
        printf("alarm_reader exit!\n");
    
        return (void *) 0;
    }
    
    int
    alarm_add(int id, int arg1, int arg2)
    {
        static int i = 0;
        alarm_t dat = { 0 };
        if (i < NUM_ALM) {
            dat.timestamp = time(NULL);
            dat.alarmid = id;
            dat.arg1 = arg1;
            dat.arg2 = arg2;
    
            if (&roller[i]) {
                memcpy(&roller[i], &dat, sizeof(alarm_t));
                if (i + 1 < NUM_ALM)
                    roller[i + 1].alarmid = 0;
                else
                    roller[0].alarmid = 0;
                pthread_cond_signal(&cv);
                prtf("added id %d, arg1 %d, arg2 %d @%d\n", roller[i].alarmid, roller[i].arg1, roller[i].arg2, i);
                i++;
            }
        }
        else {
            i = 0;
        }
        return 0;
    }
    
    int
    next_alarm_read(alarm_t * res)
    {
        static int i = 0;
        //static long prev_time = 0;
    
        if (!res)
            return ERROR;
    
        if (i < NUM_ALM) {
            if (roller[i].alarmid != 0) {
                prtf("next_alarm_read() reading @%d\n", i);
                res->timestamp = roller[i].timestamp;
                res->alarmid = roller[i].alarmid;
                res->arg1 = roller[i].arg1;
                res->arg2 = roller[i].arg2;
                //prev_time = roller[i].timestamp;
                i++;
            }
            else {
                prtf("next_alarm_read() @%d is %d,return ERROR\n", i, roller[i].alarmid);
    
                return ERROR;
            }
        }
        else {
            i = 0;
        }
        return OK;
    }