int spsc_trypush_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); } // check index, used flag, and data assert(q->push_idx == 26); void *offset = (char *)(q->array + 12*q->elem_size); size_t used = *(size_t *)offset; assert(used == 1); char *c = (char *)offset + sizeof(size_t); assert(*c == 'M'); return 1; }
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; }
int spsc_new_fail() { cqueue_spsc *q; // fail on capacity q = cqueue_spsc_new(SIZE_MAX/2 + 2, sizeof(int)); assert(!q); // fail on elem_size q = cqueue_spsc_new(32, 0); assert(!q); // fail on memory allocation q = cqueue_spsc_new(1, SIZE_MAX-sizeof(_Atomic size_t)-LEVEL1_DCACHE_LINESIZE); assert(!q); // fail on overflow of capacity * elem_size q = cqueue_spsc_new(SIZE_MAX/LEVEL1_DCACHE_LINESIZE, sizeof(int)); assert(!q); return 1; }
int spsc_new_pass() { cqueue_spsc *q; ptrdiff_t d; q = cqueue_spsc_new(26, LEVEL1_DCACHE_LINESIZE-sizeof(size_t)); assert(q); assert(q->capacity == 32); // round up to power of 2 assert(q->elem_size == LEVEL1_DCACHE_LINESIZE); // round up to cacheline // check initialization assert(q->push_idx == 0); assert(q->pop_idx == 0); for(size_t i=0; i < q->capacity; i++) { size_t used = *(size_t *)(q->array + i*q->elem_size); assert(used == 0); } // check struct layout d = ((char *)(void *)q - (char *)(void *)&q->capacity); assert(d == 0); // capacity is the first member d = (char *)(void *)&q->push_idx - (char*)(void *)&q->capacity; assert(d == LEVEL1_DCACHE_LINESIZE); // should be 1 cacheline due to padding d = (char *)(void *)&q->pop_idx - (char *)(void *)&q->push_idx; assert(d == LEVEL1_DCACHE_LINESIZE); // should be 1 cacheline due to padding // check deletion cqueue_spsc_delete(&q); assert(!q); // check elem_size padding rounding up q = cqueue_spsc_new(26, LEVEL1_DCACHE_LINESIZE-sizeof(size_t)+1); assert(q); assert(q->elem_size == 2*LEVEL1_DCACHE_LINESIZE); // round up to 2 cachelines 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; }
/*! allocate and initialize queues, etc \param[in, out] context \returns 1 on success, 0 on failure */ int arpd_init(struct thread_context *context) { struct arpd_data *data; int rv; assert(context); data = context->data; context->msg_q = squeue_new(data->msg_q_capacity, data->msg_q_elem_size); if (!context->msg_q) return 0; context->pkt_xmit_q = cqueue_spsc_new(data->xmit_q_capacity, data->xmit_q_elem_size); if (!context->pkt_xmit_q) { squeue_delete(&context->msg_q); return 0; } rv = xmit_queue_init(context->pkt_xmit_q, &context->shared->inet_info->addr, &context->shared->if_info->mac); if (!rv) { squeue_delete(&context->msg_q); return 0; } context->pkt_recv_q = tqueue_new(data->recv_q_transactions, data->recv_q_actions_per_transaction); if (!context->pkt_recv_q) { squeue_delete(&context->msg_q); cqueue_spsc_delete(&context->pkt_xmit_q); return 0; } data->arp_cache = arp_cache_new(context); if (!data->arp_cache) { squeue_delete(&context->msg_q); cqueue_spsc_delete(&context->pkt_xmit_q); tqueue_delete(&context->pkt_recv_q); return 0; } return 1; }