int spsc_trypop_slot_pass() { cqueue_spsc *q; int i; char data; char *p; q = cqueue_spsc_new(26, sizeof(char)); assert(q); // insert dummy data for testing for(i=0, data='A'; i < 26; i++, data++) { p = cqueue_spsc_trypush_slot(q); assert(p); *p = data; cqueue_spsc_push_slot_finish(q); } // pop some data so we're not at the start for(i=0; i < 13; i++) { p = cqueue_spsc_trypop_slot(q); assert(p); cqueue_spsc_pop_slot_finish(q); } // check index, used flag, and data assert(q->pop_idx == 13); void *offset = (char *)(q->array + 12*q->elem_size); size_t used = *(size_t *)offset; assert(used == 0); assert(*p == 'M'); return 1; }
// initialize each slot in the xmit queue with ethernet header and // arp data that is consistent between requests and replies int xmit_queue_init(cqueue_spsc *q, struct in_addr *my_ip, struct ether_addr *my_mac) { struct xmit_queue_slot *s; struct arp_pkt *arp; size_t ether_arp_len, i, start_idx; ether_arp_len = sizeof(struct ether_header) + sizeof(struct arp_pkt); if (ether_arp_len < ETHER_MIN_LEN - ETHER_CRC_LEN) ether_arp_len = ETHER_MIN_LEN - ETHER_CRC_LEN; start_idx = q->push_idx; for (i=0; i < q->capacity; i++) { s = cqueue_spsc_push_slot(q); if (!s) return 0; s->len = ether_arp_len; /* ethernet header */ memcpy(s->ether_h.ether_shost, my_mac, sizeof(struct ether_addr)); s->ether_h.ether_type = ARP_ETHERTYPE; // ether_dhost changes in a request or reply /* arp */ arp = (struct arp_pkt *)s->data; arp->arp_h.ar_hrd = ARP_HAF_ETHER; arp->arp_h.ar_pro = IP4_ETHERTYPE; arp->arp_h.ar_hln = ETHER_ADDR_LEN; arp->arp_h.ar_pln = sizeof(struct in_addr); // ar_op changes for request/reply memcpy(&arp->sha, my_mac, sizeof(struct ether_addr)); arp->spa.s_addr = my_ip->s_addr; // tha and tpa change in a request/reply cqueue_spsc_push_slot_finish(q); // reset the slot used flag without touching the data s = cqueue_spsc_pop_slot(q); if (!s) return 0; cqueue_spsc_pop_slot_finish(q); } // detect if something else pushed while we were initializing if (q->push_idx != start_idx) return 0; return 1; }
/*! Warning: this function relies on internal knowledge of cqueue_spsc to optimize num_actions */ tqueue *tqueue_new(size_t num_transactions, size_t num_actions) { tqueue *q; transaction *t; size_t transaction_size; if (!num_transactions) return NULL; if (!num_actions) return NULL; // check overflow if (num_actions > (SIZE_MAX - sizeof(transaction)) / sizeof (void *)) return NULL; transaction_size = sizeof(transaction) + num_actions * sizeof(void *); q = malloc(sizeof(tqueue)); if (!q) return NULL; q->transactions = cqueue_spsc_new(num_transactions, transaction_size); if (!q->transactions) { free(q); return NULL; } q->num_transactions = num_transactions; q->num_actions = (q->transactions->elem_size - sizeof(_Atomic size_t) - sizeof(transaction)) / sizeof(void *); // initialize the transactions while ((t = cqueue_spsc_trypush_slot(q->transactions)) != NULL) { t->write_idx = 0; t->read_idx = 0; cqueue_spsc_push_slot_finish(q->transactions); } while ((t = cqueue_spsc_trypop_slot(q->transactions)) != NULL) { cqueue_spsc_pop_slot_finish(q->transactions); } return q; }
int tqueue_remove(tqueue *q, transaction **tp, void **p) { assert(q); transaction *t = *tp; if (!t) { t = cqueue_spsc_trypop_slot(q->transactions); if (!t) // transaction queue empty return TQUEUE_EMPTY; } *p = t->actions[t->read_idx]; t->read_idx++; if (t->read_idx == t->write_idx) { // we've read everything t->write_idx = 0; t->read_idx = 0; t = NULL; cqueue_spsc_pop_slot_finish(q->transactions); return TQUEUE_TRANSACTION_EMPTY; } return TQUEUE_SUCCESS; }