int vector_get(const vector *v, unsigned long long idx, void **e) { if (v == NULL) { v_error("v is NULL\n"); return -1; } if (idx >= v->count) { if (VECTOR_DIE_ON_OOB) { v_die("index %llu out-of-bounds, v->count=%llu\n", idx, v->count); } v_error("index %llu out-of-bounds, v->count=%llu\n", idx, v->count); return -1; } /* No need for flushing / failure recovery here; not changing anything, * so user can just call vector_get() again. */ *e = v->data[idx]; //v_debug("got pointer %p (string %s) from slot %llu (count=%llu, size=%llu); " // "string may not make sense because now we're storing vtes in vector\n", // e, (char *)(*e), idx, v->count, v->size); v_debug("got pointer %p from slot %llu (count=%llu, size=%llu)\n", e, idx, v->count, v->size); return 0; }
/* This function allows NULL pointers for the element put into the array, * for now. */ int vector_set(vector *v, unsigned long long idx, void *e, void **old_e) { if (v == NULL) { v_error("v is NULL\n"); return -1; } if (idx >= v->count) { if (VECTOR_DIE_ON_OOB) { v_die("index %llu out-of-bounds, v->count=%llu\n", idx, v->count); } v_error("index %llu out-of-bounds, v->count=%llu\n", idx, v->count); return -1; } /* Set operation happens "atomically," as soon as the pointer that we * set gets flushed to memory. */ *old_e = v->data[idx]; v->data[idx] = e; kp_flush_range(&(v->data[idx]), sizeof(void *), v->use_nvm); v_debug("stored element %s in slot %llu (count=%llu, size=%llu)\n", (char *)(v->data[idx]), idx, v->count, v->size); return 0; }
int vector_search_linear(const vector *v, size_t offset, uint64_t key, uint64_t *idx) { uint64_t i, val; if (v == NULL) { v_error("v is NULL\n"); return -1; } /* Only search up to count, not size! * v->count is an unsigned long long, which should be the same as a * uint64_t on most/all platforms. */ for (i = 0; i < v->count; i++) { val = vector_get_uint64_t(v, offset, i); if (val == key) { *idx = i; v_debug("found search key %ju at idx=%ju\n", key, *idx); return 0; } } v_debug("searched through all %llu elements, did not find search key " "%ju\n", v->count, key); return 1; }
void vector_destroy(vector **v) { bool use_nvm; if (!v) { v_error("got NULL argument unexpectedly; returning\n"); return; } if (*v) { use_nvm = (*v)->use_nvm; (*v)->state = STATE_DEAD; kp_flush_range((void *)&((*v)->state), sizeof(ds_state), use_nvm); if ((*v)->data) { v_debug("testing 0: (*v)->data = %p\n", (*v)->data); kp_free((void **)&((*v)->data), use_nvm); //sets (*v)->data to NULL #ifdef VECTOR_ASSERT v_debug("testing 1: (*v)->data = %p\n", (*v)->data); if ((*v)->data != NULL) { v_die("kp_free() did not set (*v)->data to NULL as expected!\n"); } #endif } v_debug("testing 2: *v = %p\n", *v); kp_free((void **)(v), use_nvm); //sets *v to NULL after free #ifdef VECTOR_ASSERT v_debug("testing 3: *v = %p\n", *v); if (*v != NULL) { v_die("kp_free() did not set *v to NULL as expected!\n"); } #endif } v_debug("freed vector's array and vector struct itself\n"); }
unsigned long long vector_count(const vector *v) { if (v == NULL) { v_error("v is NULL\n"); return -1; //vector_count isn't supposed to return an error, oh well } v_debug("returning count=%llu (size=%llu)\n", v->count, v->size); return v->count; }
int vector_create(vector **v, unsigned long long size, bool use_nvm) { unsigned long long init_size; v_debug("creating new vector with use_nvm=%s\n", use_nvm ? "true" : "false"); /* Use calloc if use_nvm is true, otherwise just malloc: */ kp_kpalloc((void **)v, sizeof(vector), use_nvm); if (*v == NULL) { v_error("kp_kpalloc(vector) failed\n"); return -1; } init_size = size ? size : VECTOR_INIT_SIZE; v_debug("using init_size=%llu for vector\n", init_size); kp_kpalloc((void **)&((*v)->data), sizeof(void *) * init_size, use_nvm); if ((*v)->data == NULL) { v_error("kp_kpalloc(data) failed\n"); vector_destroy(v); #ifdef VECTOR_ASSERT if (*v != NULL) { v_die("vector_destroy() did not set *v to NULL as expected!\n"); } #endif return -1; } (*v)->size = init_size; (*v)->count = 0; (*v)->use_nvm = use_nvm; /* "CDDS": flush, set state, and flush again. */ kp_flush_range((void *)*v, sizeof(vector) - sizeof(ds_state), use_nvm); (*v)->state = STATE_ACTIVE; kp_flush_range((void *)&((*v)->state), sizeof(ds_state), use_nvm); v_debug("successfully created new vector\n"); return 0; }
void vector_free_contents(vector *v) { unsigned long long i, count; if (v == NULL) { v_error("v is NULL\n"); return; } count = v->count; for (i = 0; i < count; i++) { if (v->data[i]) { v_debug("freeing element %s from slot %llu\n", (char *)(v->data[i]), i); kp_free(&(v->data[i]), v->use_nvm); //sets v->data[i] to NULL } else { /* could be a deleted value? */ v_debug("NULL pointer in array, not freeing it\n"); } } v_debug("successfully freed %llu elements from vector\n", count); }
void main(int argc, char *argv[]) { // --- define training and testing sets --- unsigned int init = static_cast<unsigned int> ( time(NULL) ); srand( init ); int i, pair; char str[8] = {0}; // number of input-output vector pairs in the training set int numberOfTrainingPairs = HIDDEN; // number of input-output vector pairs in the testing set int numberOfTestingPairs = 6; // training set vector<vector<double>> trainingInputVector; vector<vector<double>> trainingOutputVector; // testing set vector<vector<double>> testingInputVector; vector<vector<double>> testingOutputVector; // --- read in the training set --- try { ifstream hfile( "train.txt" ); if( !hfile.good() ) { printf( "File not exists\n" ); return; } // read each line of the file // each line contains an input-output vector pair of the form: // // input_0 input_1 ... input_n output_0 output_1 ... output_n trainingInputVector.resize( numberOfTrainingPairs ); trainingOutputVector.resize( numberOfTrainingPairs ); for( pair = 0; pair < numberOfTrainingPairs; pair++) { string line; getline( hfile, line ); istringstream split(line); string token; // read in the input vector for( i = 0; i < INPUT; i++ ) { getline( split, token, ' ' ); trainingInputVector[pair].push_back( atof( token.c_str() ) ); } // read in the output vector for( i = 0; i < OUTPUT; i++ ) { getline( split, token, ' ' ); trainingOutputVector[pair].push_back( atof( token.c_str() ) ); } } } catch (exception e) { printf( e.what() ); } // --- read in the testing set --- try { ifstream hfile( "test.txt" ); if( !hfile.good() ) { printf( "File not exists\n" ); return; } // read each line of the file // each line contains an input-output vector pair of the form: // // input_0 input_1 ... input_n output_0 output_1 ... output_n testingInputVector.resize( numberOfTestingPairs ); testingOutputVector.resize( numberOfTestingPairs ); for( pair = 0; pair < numberOfTestingPairs; pair++ ) { string line; getline( hfile, line ); istringstream iss(line); string token; // read in the input vector for( int i = 0; i < INPUT; i++ ) { getline(iss, token, ' '); testingInputVector[pair].push_back( atof(token.c_str() ) ); } // read in the output vector for( int i = 0; i < OUTPUT; i++ ) { getline( iss, token, ' ' ); testingOutputVector[pair].push_back( atof( token.c_str() ) ); } } } catch ( exception e ) { printf( e.what() ); } // network init CounterpropNetwork *c = new CounterpropNetwork(); // train the network c->training( trainingInputVector, trainingOutputVector ); printf("Testing network...\n"); // test the network double error = 0; vector<double> v_error(OUTPUT); vector<int> v_numCorrect(OUTPUT); for( i = 0; i < numberOfTestingPairs; i++ ) { vector<double> outputVector( OUTPUT ); outputVector = c->testing( testingInputVector[i] ); printf( "Output for line #%d: ", i+1 ); for( int x = 0; x < OUTPUT; ++x ) { printf( "%.1f ", outputVector[x] ); } printf( "(Real: " ); for( int x = 0; x < OUTPUT; ++x ) { printf( "%.1f ", testingOutputVector[i][x] ); } printf( ")" ); printf( "\n" ); for( int x = 0; x < OUTPUT; ++x ) { error = fabs( outputVector[x] - testingOutputVector[i][x] ); if( error <= c->m_dbTolerance ) v_numCorrect[x]++; } } // output testing accuracy for( int x = 0; x < OUTPUT; ++x ) { v_error[x] = (double) v_numCorrect[x] / numberOfTestingPairs * 100.00; printf( "Accuracy Output %d = %.0f%%\n", x, v_error[x] ); } }
void generate_voronoi(struct point *p_array, int num){ init_voronoi(); // printf("init memory succed\n"); X0=0; Y0=0; X1=2000; Y1=2000; if(num >= POINT_POOL_SIZE){ v_error("the number of points is out of bound"); return; } int i; for (i=0; i<num; i++){ push_point(p_array[i]); // printf("%f, %f\n", p_array[i].x, p_array[i].y); if(p_array[i].x < X0) X0 = p_array[i].x; if(p_array[i].y < Y0) Y0 = p_array[i].y; if(p_array[i].x > X1) X1 = p_array[i].x; if(p_array[i].y > Y1) Y1 = p_array[i].y; } add_virtual_obstacles(); points_quicksort(); /* X0=0; */ /* Y0=0; */ /* X1=2500; */ /* Y1=2500; */ // printf("quicksort finished \n"); // printf("%f, %f, %f, %f\n\n\n", X0, Y0, X1,Y1); //add 20% margins to the bounding box /* double dx = (X1-X0+1)/5.0; */ /* double dy = (Y1-Y0+1)/5.0; */ /* X0 -= dx; */ /* X1 += dx; */ /* Y0 -= dy; */ /* Y1 += dy; */ // printf("%f, %f, %f, %f\n\n\n", X0, Y0, X1,Y1); while (!is_pp_empty()){ if(!is_ep_empty()){ event_t * top_e = top_event(); point_t top_p = top_point(); if(top_e->x <= top_p.x){ process_event(); }else{ process_point(); } }else{ process_point(); } } // print_arc_list(); while(!is_ep_empty()){ process_event(); } finish_edges(); rebuild_seg_list(&seg_list); int k; char *buf=malloc(sizeof(char)*1024); int pcount=0; for(k=0;k<seg_list.segs_count;k++){ pcount+=sprintf(&buf[pcount],"%d;%d;%d;%d",k,seg_list.segs[k]->start.x,seg_list.segs[k]->start.y,seg_list.segs[k]->end.x,seg_list.segs[k]->end.y); } send_raw_eth_chn(34,buf,603); }
int vector_delete(vector *v, unsigned long long idx, void **e) { unsigned long long i; int flush_size = 0; if (v == NULL) { v_error("v is NULL\n"); return -1; } if (idx >= v->count) { if (VECTOR_DIE_ON_OOB) { v_die("index %llu out-of-bounds, v->count=%llu\n", idx, v->count); } v_error("index %llu out-of-bounds, v->count=%llu\n", idx, v->count); return -1; } /* Don't free the element to be deleted, but set *e to point to it * so that the caller can free it. Then, shift all of the other * elements in the array down one slot: */ v_debug("deleting element %s from slot %llu\n", (char *)v->data[idx], idx); if (e) { *e = v->data[idx]; } /* Start at the removed index and shift elements backwards one at a * time. This is somewhat "repeatable" - if a failure occurs while * we're doing this, then we can either remember the shift index, or * look through the array for duplicates, and then resume where we left * off. We flush after every single shift (ouch!) so that no data can * be lost. This is similar (but opposite) to vector_insert() above. */ for (i = idx; i < v->count - 1; i++) { v->data[i] = v->data[i+1]; kp_flush_range(&(v->data[i]), sizeof(void *), v->use_nvm); v_debug("shifted element %s from slot %llu down to slot %llu\n", (char *)(v->data[i]), i+1, i); } v->count--; kp_flush_range((void *)&(v->count), sizeof(unsigned long long), v->use_nvm); v_debug("now count=%llu, size=%llu (resize factor=%u)\n", v->count, v->size, VECTOR_RESIZE_FACTOR); /* Shrink the array if the number of used slots falls below the number * of allocated slots divided by the resize factor times 2. We double * the resize factor when checking this condition, but only shrink the * array by a single resize factor, to avoid "pathological" behavior * where the vector reaches some size and then the client repeatedly * adds one element and deletes one element, causing a resize on every * operation (note: this analysis is not scientific nor empirical). * * In the condition below, <= causes resizing to happen a bit earlier * and seems better than just <. With VECTOR_RESIZE_FACTOR = 2, this * logic causes the array to be cut in half when the number of elements * is decreased to 1/4 of the number of allocated slots. */ if ((v->size > VECTOR_INIT_SIZE) && (v->count <= v->size / (VECTOR_RESIZE_FACTOR * 2))) { v_debug("count %llu is <= %llu, shrinking array\n", v->count, v->size / (VECTOR_RESIZE_FACTOR * 2)); /* See notes about flush_size in vector_append() above. */ if (v->use_nvm) { flush_size = offsetof(vector, count) - offsetof(vector, data); #ifdef VECTOR_ASSERT if (flush_size < (sizeof(void **) + sizeof(unsigned long long))) { v_die("got unexpected flush_size %d! offsetof(count)=%zu, " "offsetof(data)=%zu\n", flush_size, offsetof(vector, count), offsetof(vector, data)); } #endif } /* We set the vector's new size first, then set its data pointer, and * then finally flush them both to memory (if use_nvm is true). See * the notes in vector_append() for this. */ /* Leak window begin: */ v->size /= VECTOR_RESIZE_FACTOR; //inverse of vector_append() kp_realloc((void **)&(v->data), sizeof(void*) * v->size, v->use_nvm); kp_flush_range(&(v->data), flush_size, v->use_nvm); /* Leak window end. If we fail after the flush has returned, then * the next call to vector_delete() will skip the resizing step. */ if (v->data == NULL) { v_die("kp_realloc(array) failed\n"); } v_debug("shrunk array, now has size %llu (%llu slots)\n", sizeof(void*) * v->size, v->size); } return 0; }
/* CHECK - TODO: how consistent / durable / atomic / recoverable is this * function??? * * This function allows NULL pointers for the element put into the array, * for now. */ uint64_t vector_insert(vector *v, const void *e, vector_comparator cmp) { unsigned long long orig_size, new_size; uint64_t insert_idx, shift_idx; int flush_size = 0; /* HEADS UP: this function is mostly the same as vector_append(), so * if you update one, you should probably update the other! */ if (v == NULL) { v_error("v is NULL\n"); return -1; } if (v->count == VECTOR_MAX_COUNT) { v_error("hit maximum vector length: v->count=%llu\n", v->count); return -1; } #ifndef UNSAFE_COMMIT //#ifdef VECTOR_ASSERT #if 0 /* This assert only makes sense if we're not garbage collecting or * otherwise removing things from vectors. Note that the resizing * code doesn't contain any explicit checks for this (it will * happily reduce the size of the vector below the initial size * if you remove enough things); this bhavior should probably * change, but whatever. */ if (v->size < VECTOR_INIT_SIZE) { v_die("vector size %llu is less than initial size %d!!\n", v->size, VECTOR_INIT_SIZE); } #endif #endif kp_todo("factor out the resizing code that's common to both append " "and insert, dummy!\n"); /* When the last array slot is exhausted, increase the size of the * array by multiplying it by the resize factor. * Resizing the array consists of two steps: A) re-allocing the memory * region, and B) setting the new size of the vector. A) is repeatable, * but unfortunately could result in a memory leak if we don't correctly * remember the pointer AND remember the new size! To minimize the leak * window here, we immediately flush both the pointer and the size (which * should be adjacent to each other in the struct!) after the realloc. * We calculate the size of the flush that we need to perform by using * the offsetof operator, because the compiler may insert padding between * members that we don't know about. This code assumes that the order of * the members in the struct is [data, size, count]. * * ... */ if (v->size == v->count) { #ifdef VECTOR_RESIZE_PRINT v_print("v->size hit v->count = %llu; insert resizing!!!\n", v->size); #endif v_debug("v->size hit v->count = %llu; resizing!!!\n", v->size); /* Check if multiplying v->size by VECTOR_RESIZE_FACTOR will put * it over the VECTOR_MAX_COUNT: */ if (v->size > (VECTOR_MAX_COUNT / VECTOR_RESIZE_FACTOR)) { v_debug("vector size (%llu) times resize factor (%d) would " "overflow max count (%llu), so setting size directly " "to max count\n", v->size, VECTOR_RESIZE_FACTOR, VECTOR_MAX_COUNT); new_size = VECTOR_MAX_COUNT; } else { new_size = v->size * VECTOR_RESIZE_FACTOR; } #ifdef VECTOR_RESIZE_PRINT v_print("calculated new_size=%llu (insert)\n", new_size); #endif orig_size = v->size; #ifdef UNSAFE_COMMIT if (new_size > UNSAFE_COMMIT_LOG_SIZE) { v_print("WARNING: new vector size is greater than %d, probably " "means that we're resizing the commit_log vector!!\n", UNSAFE_COMMIT_LOG_SIZE); } #endif #ifdef V_ASSERT if (new_size > 100) { v_debug("WARNING: resizing vector to new_size=%llu\n", new_size); } #endif /* We expect the flush_size to be 12, but the compiler could possibly * insert padding that changes this. On brief examination, no padding * is inserted and both the data pointer and the size are flushed in * a single flush. * todo: could put the following code segment in its own "pcm_realloc()" * function... */ if (v->use_nvm) { /* We only need to flush data and size; count comes _after_ size, * so use it as the end of the range to flush. */ flush_size = offsetof(vector, count) - offsetof(vector, data); //v_print("calculated flush_size=%d from offsetof(count)=%u, " // "offsetof(data)=%u\n", flush_size, offsetof(vector, count), // offsetof(vector, data)); #ifdef VECTOR_ASSERT if (flush_size < (sizeof(void **) + sizeof(unsigned long long))) { v_die("got unexpected flush_size %d! offsetof(count)=%zu, " "offsetof(data)=%zu\n", flush_size, offsetof(vector, count), offsetof(vector, data)); } //v_print("calculated flush_size %d from offsetof(count)=%u, " // "offsetof(data)=%d\n", flush_size, // offsetof(vector, count), offsetof(vector, data)); #endif } /* Leak window begin. Note that kp_realloc() doesn't flush internally, * because we want to flush both the data and the size in the same * cache line to get consistency * Also note that we don't currently GUARANTEE this, if the compiler * happens to allocate v->data and v->size in two different cache * lines. */ kp_realloc((void **)&(v->data), sizeof(void*) * new_size, v->use_nvm); v->size = new_size; kp_flush_range(&(v->data), flush_size, v->use_nvm); //flush both data and size! /* Leak window end. If we fail after the flush() has returned, * then the next call to vector_append() will skip the resizing * step. */ if (v->data == NULL) { v_error("kp_realloc(array) failed\n"); /* Best effort on failure: reset size to what it was before, * and let caller handle the rest. */ v->size = orig_size; return -1; } v_debug("re-allocated array, now has size %llu (%llu slots)\n", sizeof(void*) * v->size, v->size); } /* We expect that this function will often be an append anyway, so * we start at the end of the array and then search backwards for the * index to insert into. */ insert_idx = v->count; /* The comparator function returns positive if e1 > e2. By using * > and not >= here, we will stop as early as possible, so if * elements happen to be equal to each other then the later elements * will be further along in the array. */ while (insert_idx > 0 && cmp(v->data[insert_idx-1], e) > 0) { insert_idx--; } //v_print("set insert_idx=%llu (count=%llu)\n", insert_idx, v->count); /* Start at the back of the array again and shift elements forward one * at a time. This is somewhat "repeatable" - if a failure occurs while * we're doing this, then we can either remember the shift index, or * look through the array for duplicates, and then resume where we left * off. We flush after every single shift (ouch!) so that no data can * be lost. */ shift_idx = v->count; while (shift_idx > insert_idx) { //v_print("shifting element from %llu to %llu\n", shift_idx-1, shift_idx); v->data[shift_idx] = v->data[shift_idx - 1]; kp_flush_range(&(v->data[shift_idx]), sizeof(void *), v->use_nvm); shift_idx--; } //v_print("now inserting new element into idx=%llu\n", insert_idx); /* Use two flushes here to make this "repeatable" - if we fail after * the first set + flush, there are no real effects (well, this was * true for append... is it any different for insert??). */ v->data[insert_idx] = (void *)e; kp_flush_range(&(v->data[insert_idx]), sizeof(void *), v->use_nvm); v->count++; kp_flush_range(&(v->count), sizeof(unsigned long long), v->use_nvm); //v_print("stored new element %s in slot %llu (now count=%llu, size=%llu)\n", // (char *)(v->data[insert_idx]), insert_idx, v->count, v->size); //v_print("stored new element %p in slot %llu (now count=%llu, size=%llu)\n", // v->data[insert_idx], insert_idx, v->count, v->size); return insert_idx; }
/* Ideally, vector_append would be consistent, durable, and _atomic_, which * would mean that it doesn't have to be _recovered_ after a failure. This * is probably possible by following the instructions in "A Lock-Free * Dynamically Resizable Array." However, this would require a significant * restructuring of the vector code that we already have, so we won't do * this for now. Instead, when the vector needs resizing, we focus on making * it _recoverable_, rather than atomic; when the vector doesn't need resizing, * then append is consistent and durable and _repeatable_, rather than atomic. * * Note that vector_append is NOT thread-safe or "lock-free" - high-level * synchronization is needed so that only one append occurs at a time! * * Relevant links: * http://www2.research.att.com/~bs/lock-free-vector.pdf * https://parasol.tamu.edu/~peterp/slides/opodis06.pdf * Intel patent, 8006064: "Lock-free vector utilizing a resource allocator...": * http://www.google.com/patents/US8006064?printsec=abstract#v=onepage&q&f=false * http://www.ibm.com/developerworks/aix/library/au-intelthreadbuilding/index.html?ca=drs- * http://software.intel.com/en-us/blogs/2009/04/09/delusion-of-tbbconcurrent_vectors-size-or-3-ways-to-traverse-in-parallel-correctly/ * * This function allows NULL pointers for the element put into the array, * for now. * * NOTE: for version 3.1 (simultaneous merges with conflict detection), I * hacked this interface to return the element that used to be the last * element in the vector, before we appended this one. Hmmm... */ int vector_append(vector *v, const void *e, void **previous_tail) { unsigned long long orig_size, new_size; int flush_size = 0; /* HEADS UP: this function is mostly the same as vector_insert(), so * if you update one, you should probably update the other! */ if (v == NULL) { v_error("v is NULL\n"); return -1; } if (v->count == VECTOR_MAX_COUNT) { v_error("hit maximum vector length: v->count=%llu\n", v->count); return -1; } #ifndef UNSAFE_COMMIT //#ifdef VECTOR_ASSERT #if 0 /* This assert only makes sense if we're not garbage collecting or * otherwise removing things from vectors. Note that the resizing * code doesn't contain any explicit checks for this (it will * happily reduce the size of the vector below the initial size * if you remove enough things); maybe this behavior should change, * but whatever. */ if (v->size < VECTOR_INIT_SIZE) { v_die("vector size %llu is less than initial size %d!!\n", v->size, VECTOR_INIT_SIZE); } #endif #endif /* When the last array slot is exhausted, increase the size of the * array by multiplying it by the resize factor. * Resizing the array consists of two steps: A) re-allocing the memory * region, and B) setting the new size of the vector. A) is repeatable, * but unfortunately could result in a memory leak if we don't correctly * remember the pointer AND remember the new size! To minimize the leak * window here, we immediately flush both the pointer and the size (which * should be adjacent to each other in the struct!) after the realloc. * We calculate the size of the flush that we need to perform by using * the offsetof operator, because the compiler may insert padding between * members that we don't know about. This code assumes that the order of * the members in the struct is [data, size, count]. * * ... */ if (v->size == v->count) { #ifdef VECTOR_RESIZE_PRINT v_print("v->size hit v->count = %llu; append resizing!!!\n", v->size); #endif v_debug("v->size hit v->count = %llu; resizing!!!\n", v->size); /* Check if multiplying v->size by VECTOR_RESIZE_FACTOR will put * it over the VECTOR_MAX_COUNT: */ if (v->size > (VECTOR_MAX_COUNT / VECTOR_RESIZE_FACTOR)) { v_debug("vector size (%llu) times resize factor (%d) would " "overflow max count (%llu), so setting size directly " "to max count\n", v->size, VECTOR_RESIZE_FACTOR, VECTOR_MAX_COUNT); new_size = VECTOR_MAX_COUNT; } else { new_size = v->size * VECTOR_RESIZE_FACTOR; } #ifdef VECTOR_RESIZE_PRINT v_print("calculated new_size=%llu (append)\n", new_size); #endif orig_size = v->size; #ifdef UNSAFE_COMMIT if (new_size > UNSAFE_COMMIT_LOG_SIZE) { v_print("WARNING: new vector size is greater than %d, probably " "means that we're resizing the commit_log vector!!\n", UNSAFE_COMMIT_LOG_SIZE); } #endif #ifdef V_ASSERT if (new_size > 100) { v_debug("WARNING: resizing vector to new_size=%llu\n", new_size); } #endif /* We expect the flush_size to be 12, but the compiler could possibly * insert padding that changes this. On brief examination, no padding * is inserted and both the data pointer and the size are flushed in * a single flush. * todo: could put the following code segment in its own "pcm_realloc()" * function... */ if (v->use_nvm) { /* We only need to flush data and size; count comes _after_ size, * so use it as the end of the range to flush. */ flush_size = offsetof(vector, count) - offsetof(vector, data); #ifdef VECTOR_ASSERT if (flush_size < (sizeof(void **) + sizeof(unsigned long long))) { v_die("got unexpected flush_size %d! offsetof(count)=%zu, " "offsetof(data)=%zu\n", flush_size, offsetof(vector, count), offsetof(vector, data)); } //v_print("calculated flush_size %d from offsetof(count)=%u, " // "offsetof(data)=%d\n", flush_size, // offsetof(vector, count), offsetof(vector, data)); #endif } /* Leak window begin. Note that kp_realloc() doesn't flush internally, * because we want to flush both the data and the size in the same * cache line to get consistency * Also note that we don't currently GUARANTEE this, if the compiler * happens to allocate v->data and v->size in two different cache * lines. */ kp_realloc((void **)&(v->data), sizeof(void*) * new_size, v->use_nvm); v->size = new_size; kp_flush_range(&(v->data), flush_size, v->use_nvm); //flush both data and size! /* Leak window end. If we fail after the flush() has returned, * then the next call to vector_append() will skip the resizing * step. */ if (v->data == NULL) { v_error("kp_realloc(array) failed\n"); /* Best effort on failure: reset size to what it was before, * and let caller handle the rest. */ v->size = orig_size; return -1; } v_debug("re-allocated array, now has size %llu (%llu slots)\n", sizeof(void*) * v->size, v->size); } /* The actual append part of vector_append() is repeatable: we first * fill in the element in the data array, then increment the count. * If we fail in-between these two steps, then vector_append() can * just be called again and we'll overwrite the memory area with the * same value. We do, however, have to flush the written element before * incrementing the count: we don't want the incremented count to hit * memory before the new element does. * ACTUALLY, this makes the vector_append ATOMIC, not repeatable (right?). * After a failure, if the caller didn't get a return value from this * function, then it can't be certain whether or not the append succeeded, * and so it should probably do a vector_get() to check if the append * happened or not. */ if (previous_tail) { //super hacky if (v->count > 0) { *previous_tail = v->data[v->count - 1]; } else { *previous_tail = NULL; } /* Don't need to flush; caller will do it... */ } /* Use two flushes here to make this "repeatable" - if we fail after * the first set + flush, there are no real effects. */ v->data[v->count] = (void *)e; kp_flush_range(&(v->data[v->count]), sizeof(void *), v->use_nvm); /* Do we need a memory fence right here? Only if we're flushing (so * the fence is already internal in kp_flush_range()); otherwise, * we're not concerned about anybody else seeing the count and the * element out-of-order... (right?). */ v->count++; kp_flush_range((void *)&(v->count), sizeof(unsigned long long), v->use_nvm); v_debug("stored new element %s in slot %llu (now count=%llu, size=%llu)\n", (char *)(v->data[(v->count)-1]), v->count-1, v->count, v->size); return 0; }