├── README.md ├── coarse_grained.h ├── fine_grained.h ├── lockfree_skiplists.h ├── report.pdf ├── test_coarse_grained.cpp ├── test_fine_grained.cpp └── test_lockfree.cpp /README.md: -------------------------------------------------------------------------------- 1 | # concurrent-priority-queues 2 | Implemented Concurrent Priority Queues using: 3 | * Fine grained locking over sequential heaps [[paper](http://www.research.ibm.com/people/m/michael/ipl-1996.pdf)] 4 | * Lockfree skiplist-based [[paper](http://people.csail.mit.edu/shanir/publications/Priority_Queues.pdf)] 5 | 6 | Please find a report with **implementation details and results** [[here](report.pdf)] 7 | -------------------------------------------------------------------------------- /coarse_grained.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::mutex QLock; 7 | 8 | template class coarsePriorityQueue 9 | { 10 | private: 11 | std::priority_queue CQueue; 12 | 13 | public: 14 | 15 | void insert(T value) 16 | { 17 | QLock.lock(); 18 | CQueue.push(value); 19 | QLock.unlock(); 20 | } 21 | 22 | T removeMin() 23 | { 24 | QLock.lock(); 25 | T ans = CQueue.top(); 26 | CQueue.pop(); 27 | QLock.unlock(); 28 | return ans; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /fine_grained.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template struct Node 8 | { 9 | static const int EMPTY = -1; 10 | static const int AVAILABLE = -2; 11 | 12 | int priority; 13 | T item; 14 | int statusTag; 15 | std::shared_ptr nodeLock; 16 | 17 | Node() 18 | { 19 | nodeLock = std::make_shared(); 20 | statusTag = EMPTY; 21 | } 22 | 23 | Node(T myItem, int myPriority, int threadID) 24 | { 25 | nodeLock = std::make_shared(); 26 | item = myItem; 27 | priority = myPriority; 28 | statusTag = threadID; 29 | } 30 | 31 | void swap(Node& a) 32 | { 33 | std::swap(a.priority, this->priority); 34 | std::swap(a.item, this->item); 35 | std::swap(a.statusTag, this->statusTag); 36 | } 37 | 38 | void lock() 39 | { 40 | nodeLock -> lock(); 41 | } 42 | 43 | void unlock() 44 | { 45 | nodeLock -> unlock(); 46 | } 47 | }; 48 | 49 | template class priorityQueue 50 | { 51 | private: 52 | const int ROOT = 1; 53 | int maxLimit; 54 | int next; 55 | std::vector> heapArray; 56 | std::atomic_flag heapLock = ATOMIC_FLAG_INIT; 57 | 58 | void lock() 59 | { 60 | while(heapLock.test_and_set()) 61 | ; 62 | } 63 | 64 | void unlock() 65 | { 66 | heapLock.clear(); 67 | } 68 | 69 | bool hasLeftChild(int i) 70 | { 71 | return ((2 * i) < maxLimit); 72 | } 73 | 74 | bool hasRightChild(int i) 75 | { 76 | return ((2 * i + 1) < maxLimit); 77 | } 78 | 79 | public: 80 | 81 | priorityQueue(int n) 82 | { 83 | assert(n > 0); 84 | next = 1; 85 | maxLimit = n + 1; 86 | heapArray.resize(maxLimit); 87 | } 88 | 89 | void insert(int priority, int threadID, T item = 0) 90 | { 91 | int currID = threadID; 92 | 93 | lock(); /*This is heapLock's lock()*/ 94 | 95 | int child = next++; 96 | if(child >= maxLimit) 97 | { 98 | fprintf(stderr, "Out of bounded memory, element will not be inserted\n"); 99 | next--; 100 | unlock(); 101 | return; 102 | } 103 | 104 | heapArray[child].nodeLock -> lock(); 105 | 106 | heapArray[child].item = item; 107 | heapArray[child].priority = priority; 108 | heapArray[child].statusTag = threadID; 109 | unlock(); /*This is heapLock's unlock()*/ 110 | heapArray[child].nodeLock -> unlock(); 111 | 112 | while(child > ROOT) 113 | { 114 | int parent = child / 2; 115 | heapArray[parent].nodeLock -> lock(); 116 | heapArray[child].nodeLock -> lock(); 117 | int oldChild = child; 118 | 119 | /*Checking if parent is available and current thread still owns the child node*/ 120 | if(heapArray[parent].statusTag == Node::AVAILABLE && heapArray[child].statusTag == currID) 121 | { 122 | if(heapArray[child].priority < heapArray[parent].priority) 123 | { 124 | heapArray[child].swap(heapArray[parent]); 125 | child = parent; 126 | } 127 | else 128 | { 129 | heapArray[child].statusTag = Node::AVAILABLE; 130 | heapArray[parent].nodeLock -> unlock(); 131 | heapArray[child].nodeLock -> unlock(); 132 | return; 133 | } 134 | } 135 | /*current thread is not the owner of the child node*/ 136 | else if(heapArray[child].statusTag != currID) 137 | { 138 | child = parent; 139 | } 140 | 141 | heapArray[oldChild].nodeLock -> unlock(); 142 | heapArray[parent].nodeLock -> unlock(); 143 | } 144 | 145 | if(child == ROOT) 146 | { 147 | heapArray[ROOT].nodeLock -> lock(); 148 | if(heapArray[ROOT].statusTag == currID) 149 | { 150 | heapArray[ROOT].statusTag = Node::AVAILABLE; 151 | } 152 | heapArray[ROOT].nodeLock -> unlock(); 153 | } 154 | } 155 | 156 | int removeMin(int threadID) 157 | { 158 | int currID = threadID; 159 | 160 | lock(); /*This is heapLock's lock()*/ 161 | 162 | int bottom = --next; 163 | if(bottom < 0) 164 | { 165 | fprintf(stderr, "No element available for removal\n"); 166 | next++; 167 | unlock(); 168 | return INT_MIN; 169 | } 170 | heapArray[ROOT].nodeLock -> lock(); 171 | heapArray[bottom].nodeLock -> lock(); 172 | 173 | unlock(); /*This is heapLock's unlock()*/ 174 | 175 | int minItem = heapArray[ROOT].priority; 176 | heapArray[ROOT].statusTag = Node::EMPTY; 177 | heapArray[ROOT].swap(heapArray[bottom]); 178 | heapArray[bottom].nodeLock -> unlock(); 179 | 180 | if(heapArray[ROOT].statusTag == Node::EMPTY) 181 | { 182 | heapArray[ROOT].nodeLock -> unlock(); 183 | return minItem; 184 | } 185 | 186 | heapArray[ROOT].statusTag = Node::AVAILABLE; 187 | int child = 0; 188 | int parent = ROOT; 189 | 190 | while(parent < heapArray.size() / 2) 191 | { 192 | int left = parent * 2; 193 | heapArray[left].nodeLock -> lock(); 194 | int right; 195 | 196 | if(hasRightChild(parent)) 197 | { 198 | right = (parent * 2) + 1; 199 | heapArray[right].nodeLock -> lock(); 200 | } 201 | else 202 | { 203 | right = -1; 204 | } 205 | 206 | if(heapArray[left].statusTag == Node::EMPTY) 207 | { 208 | heapArray[left].nodeLock -> unlock(); 209 | if(hasRightChild(parent)) 210 | { 211 | heapArray[right].nodeLock -> unlock(); 212 | } 213 | break; 214 | } 215 | else if(!hasRightChild(parent)) 216 | { 217 | child = left; 218 | } 219 | else if(heapArray[right].statusTag == Node::EMPTY || heapArray[left].priority < heapArray[right].priority) 220 | { 221 | heapArray[right].nodeLock -> unlock(); 222 | child = left; 223 | } 224 | else 225 | { 226 | heapArray[left].nodeLock -> unlock(); 227 | child = right; 228 | } 229 | 230 | if(heapArray[child].priority < heapArray[parent].priority && heapArray[child].statusTag != Node::EMPTY) 231 | { 232 | heapArray[parent].swap(heapArray[child]); 233 | heapArray[parent].nodeLock -> unlock(); 234 | parent = child; 235 | } 236 | else 237 | { 238 | heapArray[child].nodeLock -> unlock(); 239 | break; 240 | } 241 | } 242 | heapArray[parent].nodeLock -> unlock(); 243 | return minItem; 244 | } 245 | }; 246 | -------------------------------------------------------------------------------- /lockfree_skiplists.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int maxHeight; 9 | 10 | void setMaxHeight(int h) 11 | { 12 | maxHeight = h; 13 | } 14 | 15 | /* 16 | * Random level numbers should be generated according to geometric distribution. 17 | */ 18 | std::default_random_engine generator(time(NULL)); 19 | std::geometric_distribution distribution(0.5); 20 | int getRandomLevel() 21 | { 22 | int temp = distribution(generator) + 1; 23 | if(temp < maxHeight) 24 | { 25 | return temp; 26 | } 27 | return maxHeight; 28 | } 29 | 30 | /* 31 | * Struct for atomic markable reference 32 | */ 33 | template struct markableReference 34 | { 35 | T* next; 36 | bool marked; 37 | 38 | markableReference() 39 | { 40 | next = NULL; 41 | marked = false; 42 | } 43 | 44 | markableReference(T* node, bool mark) 45 | { 46 | next = node; 47 | marked = mark; 48 | } 49 | 50 | bool operator==(const markableReference& other) 51 | { 52 | return (next == other.next && marked == other.marked); 53 | } 54 | }; 55 | 56 | /* 57 | * The AtomicMarkableReference class 58 | */ 59 | template class atomicMarkableReference 60 | { 61 | private: 62 | /* 63 | * Since a pointer is always a POD type, even if T is of non-pod type 64 | * atomicity will be maintained 65 | */ 66 | std::atomic*> markedNext; 67 | 68 | public: 69 | atomicMarkableReference() 70 | { 71 | markedNext.store(new markableReference (NULL, false)); 72 | } 73 | 74 | atomicMarkableReference(T* nextNode, bool mark) 75 | { 76 | /* Atomically store the values*/ 77 | markedNext.store(new markableReference (nextNode, mark)); 78 | } 79 | 80 | /* 81 | * Returns the reference. load() is atomic and hence that will be the linearization point 82 | */ 83 | T* getReference() 84 | { 85 | return markedNext.load()->next; 86 | } 87 | 88 | /* 89 | * Returns the reference and update bool in the reference passed as the argument 90 | * load() is atomic and hence that will be the linearization point. 91 | */ 92 | T* get(bool *mark) 93 | { 94 | markableReference *temp = markedNext.load(); 95 | *mark = temp->marked; 96 | return temp->next; 97 | } 98 | 99 | /* 100 | * Set the variables unconditionally. load() is atomic and hence that will be the linearization point 101 | */ 102 | void set(T* newRef, bool newMark) 103 | { 104 | markableReference *curr = markedNext.load(); 105 | if(newRef != curr->next || newMark != curr->marked) 106 | { 107 | markedNext.store(new markableReference(newRef, newMark)); 108 | } 109 | } 110 | 111 | /* 112 | * CAS with reference and the marked field. load() is atomic and hence that will be the linearization point. 113 | * We take advantage of the fact that C++ has short-circuiting hence 114 | * if one of the first 2 conditions is false the atomic_compare_exchange_weak will not happen 115 | */ 116 | 117 | bool CAS(T* expected, T* newValue, bool expectedBool, bool newBool) 118 | { 119 | markableReference *curr = markedNext.load(); 120 | return(expected == curr->next && expectedBool == curr->marked && 121 | ((newValue == curr->next && newBool == curr->marked) || 122 | markedNext.compare_exchange_strong(curr, new markableReference(newValue, newBool)))); 123 | } 124 | }; 125 | 126 | template struct skipListNode 127 | { 128 | X value; 129 | int priority; 130 | int topLevel; 131 | std::atomic deleted; 132 | std::vector>> next; 133 | 134 | skipListNode() 135 | { 136 | priority = 0; 137 | deleted = false; 138 | next = std::vector>>(maxHeight + 1); 139 | topLevel = maxHeight; 140 | } 141 | 142 | skipListNode(int priority, X value, int height = maxHeight) 143 | { 144 | this->priority = priority; 145 | this->value = value; 146 | next = std::vector>>(height + 1); 147 | deleted = false; 148 | topLevel = height; 149 | } 150 | }; 151 | 152 | template class skipListQueue 153 | { 154 | private: 155 | skipListNode *head; 156 | skipListNode *tail; 157 | 158 | public: 159 | 160 | void display() 161 | { 162 | skipListNode *temp = head; 163 | while(temp != NULL) 164 | { 165 | std::cout << temp->priority << " " << temp->value << "\n"; 166 | temp = temp->next[0].getReference(); 167 | } 168 | } 169 | 170 | skipListQueue() 171 | { 172 | head = new skipListNode(INT_MIN, 0); 173 | tail = new skipListNode(INT_MAX, 0); 174 | 175 | /* 176 | * Set next of head to tail and 177 | * next of tail will be NULL, false due to default constructors 178 | */ 179 | for(int i = 0; i <= maxHeight; i++) 180 | { 181 | head->next[i].set(tail, false); 182 | } 183 | } 184 | 185 | /* 186 | * Populates the input paramater preds and succs with the 187 | * predecessors and successors of the priority value - prio 188 | * In addition it resets the links in case of a marked node. 189 | * Returns if the prio key exists. 190 | */ 191 | bool findNode(int prio, std::vector* >& preds, std::vector* >& succs) 192 | { 193 | int bottomLevel = 0; 194 | bool *mark = new bool; 195 | bool snip; 196 | skipListNode *pred = new skipListNode(); 197 | skipListNode *curr = new skipListNode(); 198 | skipListNode *succ = new skipListNode(); 199 | RETRY: 200 | while(true) 201 | { 202 | pred = head; 203 | for(int i = maxHeight; i >= bottomLevel; i--) 204 | { 205 | curr = pred->next[i].getReference(); 206 | while(true) 207 | { 208 | succ = curr->next[i].get(mark); 209 | while(mark[0] || curr->deleted.load()) 210 | { 211 | snip = pred->next[i].CAS(curr, succ, false, false); 212 | if(!snip) 213 | { 214 | goto RETRY; 215 | } 216 | curr = pred->next[i].getReference(); 217 | succ = curr->next[i].get(mark); 218 | } 219 | if(curr->priority < prio) 220 | { 221 | pred = curr; 222 | curr = succ; 223 | } 224 | else 225 | { 226 | break; 227 | } 228 | } 229 | preds[i] = pred; 230 | succs[i] = curr; 231 | } 232 | return (curr->priority == prio); 233 | } 234 | } 235 | 236 | /* 237 | * The priorities are unique and we don't insert in case of duplicate addition. 238 | * Hence, it returns true in case of successful addition and false otherwise. 239 | */ 240 | bool insert(int priority, X element = 0) 241 | { 242 | int maxLevel = getRandomLevel(); 243 | int bottomLevel = 0; 244 | std::vector* > preds(maxHeight + 1); 245 | std::vector* > succs(maxHeight + 1); 246 | while(true) 247 | { 248 | bool found = findNode(priority, preds, succs); 249 | if (found) { 250 | return false; 251 | } 252 | 253 | skipListNode *newNode = new skipListNode(priority, element, maxLevel); 254 | for(int i = bottomLevel; i <= maxLevel; i++) 255 | { 256 | newNode->next[i].set(succs[i], false); 257 | } 258 | 259 | skipListNode *pred = preds[bottomLevel]; 260 | skipListNode *succ = succs[bottomLevel]; 261 | 262 | if(!pred->next[bottomLevel].CAS(succ, newNode, false, false)) 263 | { 264 | continue; 265 | } 266 | bool *mark = new bool; 267 | newNode->next[bottomLevel].get(mark); 268 | for(int i = bottomLevel + 1; i <= maxLevel; i++) 269 | { 270 | while(true) 271 | { 272 | pred = preds[i]; 273 | succ = succs[i]; 274 | if(pred->next[i].CAS(succ, newNode, false, false)) 275 | { 276 | break; 277 | } 278 | findNode(priority, preds, succs); 279 | } 280 | 281 | } 282 | return true; 283 | } 284 | } 285 | 286 | /* 287 | * findAndMarkMin logically deletes the first undeleted node. 288 | * It traverses the bottom level and checks if the deleted field of curr node is true. 289 | * If so, then move to the next node. Else, atomically TAS the deleted field. 290 | */ 291 | skipListNode* findAndMarkMin() 292 | { 293 | skipListNode *curr = NULL; 294 | skipListNode *succ = NULL; 295 | curr = head->next[0].getReference(); 296 | while(curr != tail) 297 | { 298 | if(!curr->deleted.load()) 299 | { 300 | if (!curr->deleted.exchange(true)) 301 | { 302 | return curr; 303 | } 304 | else 305 | { 306 | curr = curr->next[0].getReference(); 307 | } 308 | } 309 | else 310 | { 311 | curr = curr->next[0].getReference(); 312 | } 313 | } 314 | return NULL; 315 | } 316 | 317 | /* 318 | * Remove marks all the predecessors and successors of priority 319 | * and calls findNode() to relink the nodes. 320 | */ 321 | bool remove(int priority) { 322 | int bottomLevel = 0; 323 | std::vector* > preds(maxHeight + 1); 324 | std::vector* > succs(maxHeight + 1); 325 | skipListNode* succ; 326 | while(true) 327 | { 328 | bool found = findNode(priority, preds, succs); 329 | if (!found) { 330 | return false; 331 | } 332 | else 333 | { 334 | skipListNode* nodeToRemove = succs[bottomLevel]; 335 | for (int level = nodeToRemove->topLevel; level >= bottomLevel+1; level--) 336 | { 337 | bool *mark = new bool(false); 338 | succ = nodeToRemove->next[level].get(mark); 339 | while (!mark[0]) 340 | { 341 | nodeToRemove->next[level].CAS(succ, succ, false, true); 342 | succ = nodeToRemove->next[level].get(mark); 343 | } 344 | } 345 | bool *mark = new bool(false); 346 | succ = nodeToRemove->next[bottomLevel].get(mark); 347 | while(true) 348 | { 349 | bool iMarkedIt = nodeToRemove->next[bottomLevel].CAS(succ, succ, false, true); 350 | succ = succs[bottomLevel]->next[bottomLevel].get(mark); 351 | if (iMarkedIt) 352 | { 353 | findNode(priority, preds, succs); 354 | return true; 355 | } 356 | else if (mark[0]) 357 | { 358 | return false; 359 | } 360 | } 361 | 362 | } 363 | } 364 | } 365 | 366 | /* 367 | * removeMin() returns the priority of the node deleted by findAndMarkMin(). 368 | * It calls remove() to physically delete the node. 369 | * In case of empty queue, it return INT_MIN. 370 | */ 371 | int removeMin() 372 | { 373 | skipListNode* node = findAndMarkMin(); 374 | if (node != NULL) 375 | { 376 | int prio = node->priority; 377 | remove(prio); 378 | return prio; 379 | } 380 | else 381 | { 382 | return INT_MIN; 383 | } 384 | } 385 | }; 386 | -------------------------------------------------------------------------------- /report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhavanajain/concurrent-priority-queues/1d2126a4bacfca331adbba3bce13b4fefb648101/report.pdf -------------------------------------------------------------------------------- /test_coarse_grained.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "coarse_grained.h" 14 | 15 | using namespace std; 16 | using namespace chrono; 17 | 18 | int n_add, n_remove, k_add, k_remove, lamda_add, lambda_remove; 19 | long long microseconds_add = 0, microseconds_remove = 0; 20 | atomic add_created, remove_created; 21 | coarsePriorityQueue *S; 22 | 23 | char* currentTime(time_t curr_time) { 24 | struct tm* local_time = localtime(&curr_time); 25 | char *ret_time = new char[100]; 26 | strftime(ret_time, 100, "%T", local_time); 27 | return ret_time; 28 | } 29 | 30 | int delay(int x) { 31 | int sd = chrono::system_clock::now().time_since_epoch().count(); 32 | default_random_engine random_gen(sd); 33 | exponential_distribution dist_1(1.0/x); 34 | return (int)dist_1(random_gen); 35 | } 36 | 37 | void removeTh(int id) { 38 | while (!remove_created.load()) {}; 39 | int th_id = id; 40 | int value; 41 | for (int i = 0; i < k_remove; i++) { 42 | auto start = std::chrono::high_resolution_clock::now(); 43 | value = S->removeMin(); 44 | auto wait_time = std::chrono::high_resolution_clock::now() - start; 45 | if(value != INT_MIN) 46 | printf("Thread %d removed %d from the queue for the %dth time\n", th_id, value, i+1); 47 | else 48 | printf("Thread %d tried remove, but empty, for the %dth time\n", th_id, i+1); 49 | fflush(stdout); 50 | microseconds_remove += std::chrono::duration_cast(wait_time).count(); 51 | sleep(delay(lambda_remove)); 52 | } 53 | pthread_exit(NULL); 54 | } 55 | 56 | void addTh(int id) { 57 | while (!add_created.load()) {}; 58 | int th_id = id; 59 | for (int i = 0; i < k_add; i++) { 60 | int value = rand()%16; 61 | auto start = std::chrono::high_resolution_clock::now(); 62 | S->insert(value); 63 | auto wait_time = std::chrono::high_resolution_clock::now() - start; 64 | printf("Thread %d inserted %d in the queue for the %dth time\n",th_id,value, i+1); 65 | microseconds_add += std::chrono::duration_cast(wait_time).count(); 66 | sleep(delay(lamda_add)); 67 | } 68 | pthread_exit(NULL); 69 | } 70 | 71 | int main() { 72 | srand(time(NULL)); 73 | freopen("inp-params.txt", "r", stdin); 74 | freopen("result-output.txt", "w", stdout); 75 | cin >> n_add >> n_remove >> k_add >> k_remove >> lamda_add >> lambda_remove; 76 | S = new coarsePriorityQueue(); 77 | add_created.store(false); 78 | remove_created.store(false); 79 | 80 | thread *add_thread = new thread[n_add]; 81 | 82 | for (int i=0; i< n_add; i++) { 83 | add_thread[i] = thread(addTh, i); 84 | } 85 | 86 | thread *rm_thread = new thread[n_remove]; 87 | for (int j=0; j< n_remove; j++) { 88 | rm_thread[j] = thread(removeTh, j); 89 | } 90 | add_created.store(true); 91 | remove_created.store(true); 92 | 93 | for(int i = 0; i < n_add; i++) 94 | add_thread[i].join(); 95 | for (int j=0; j < n_remove; j++) 96 | rm_thread[j].join(); 97 | 98 | printf("Average execution time for Insert is %lld\n", microseconds_add); 99 | fflush(stdout); 100 | printf("Average execution time for removeMin is %lld\n", microseconds_remove); 101 | fflush(stdout); 102 | return 0; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /test_fine_grained.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "fine_grained.h" 14 | 15 | using namespace std; 16 | using namespace chrono; 17 | 18 | int n_add, n_remove, k_add, k_remove, lamda_add, lambda_remove; 19 | long long microseconds_add = 0, microseconds_remove = 0; 20 | atomic add_created, remove_created; 21 | priorityQueue *S; 22 | 23 | char* currentTime(time_t curr_time) { 24 | struct tm* local_time = localtime(&curr_time); 25 | char *ret_time = new char[100]; 26 | strftime(ret_time, 100, "%T", local_time); 27 | return ret_time; 28 | } 29 | 30 | int delay(int x) { 31 | int sd = chrono::system_clock::now().time_since_epoch().count(); 32 | default_random_engine random_gen(sd); 33 | exponential_distribution dist_1(1.0/x); 34 | return (int)dist_1(random_gen); 35 | } 36 | 37 | void removeTh(int id) { 38 | while (!remove_created.load()) {}; 39 | int th_id = id; 40 | int value; 41 | for (int i = 0; i < k_remove; i++) { 42 | auto start = std::chrono::high_resolution_clock::now(); 43 | value = S->removeMin(th_id); 44 | auto wait_time = std::chrono::high_resolution_clock::now() - start; 45 | if(value != INT_MIN) 46 | printf("Thread %d removed %d from the queue for the %dth time\n", th_id, value, i+1); 47 | else 48 | printf("Thread %d tried remove, but empty, for the %dth time\n", th_id, i+1); 49 | fflush(stdout); 50 | microseconds_remove += std::chrono::duration_cast(wait_time).count(); 51 | sleep(delay(lambda_remove)); 52 | } 53 | pthread_exit(NULL); 54 | } 55 | 56 | void addTh(int id) { 57 | while (!add_created.load()) {}; 58 | int th_id = id; 59 | for (int i = 0; i < k_add; i++) { 60 | int value = rand()%16; 61 | auto start = std::chrono::high_resolution_clock::now(); 62 | S->insert(value, th_id); 63 | auto wait_time = std::chrono::high_resolution_clock::now() - start; 64 | printf("Thread %d inserted %d in the queue for the %dth time\n",th_id,value, i+1); 65 | microseconds_add += std::chrono::duration_cast(wait_time).count(); 66 | sleep(delay(lamda_add)); 67 | } 68 | pthread_exit(NULL); 69 | } 70 | 71 | int main() { 72 | srand(time(NULL)); 73 | freopen("inp-params.txt", "r", stdin); 74 | freopen("result-output.txt", "w", stdout); 75 | cin >> n_add >> n_remove >> k_add >> k_remove >> lamda_add >> lambda_remove; 76 | S = new priorityQueue(n_add*k_add); 77 | add_created.store(false); 78 | remove_created.store(false); 79 | thread *arr_add = new thread[n_add]; 80 | for (int i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "lockfree_skiplists.h" 14 | 15 | using namespace std; 16 | using namespace chrono; 17 | 18 | int n_add, n_remove, k_add, k_remove, lamda_add, lambda_remove; 19 | long long microseconds_add = 0, microseconds_remove = 0; 20 | atomic add_created, remove_created; 21 | skipListQueue *S; 22 | 23 | char* currentTime(time_t curr_time) { 24 | struct tm* local_time = localtime(&curr_time); 25 | char *ret_time = new char[100]; 26 | strftime(ret_time, 100, "%T", local_time); 27 | return ret_time; 28 | } 29 | 30 | int delay(int x) { 31 | int sd = chrono::system_clock::now().time_since_epoch().count(); 32 | default_random_engine random_gen(sd); 33 | exponential_distribution dist_1(1.0/x); 34 | return (int)dist_1(random_gen); 35 | } 36 | 37 | void removeTh(int id) { 38 | while (!remove_created.load()) {}; 39 | int th_id = id; 40 | int value; 41 | for (int i = 0; i < k_remove; i++) { 42 | auto start = std::chrono::high_resolution_clock::now(); 43 | value = S->removeMin(); 44 | time_t finish_time = time(NULL); 45 | auto wait_time = std::chrono::high_resolution_clock::now() - start; 46 | if (value == INT_MIN) { 47 | printf("At %s : EMPTY QUEUE! thread %d - trial number %d - failed removing min element\n", 48 | currentTime(finish_time), th_id, i+1); 49 | fflush(stdout); 50 | } 51 | else{ 52 | printf("At %s : thread %d - trial number %d - removed min element %d \n", 53 | currentTime(finish_time), th_id, i+1, value); 54 | fflush(stdout); 55 | } 56 | 57 | microseconds_remove += std::chrono::duration_cast(wait_time).count(); 58 | sleep(delay(lambda_remove)); 59 | } 60 | pthread_exit(NULL); 61 | 62 | } 63 | 64 | void addTh(int id) { 65 | while (!add_created.load()) {}; 66 | int th_id = id; 67 | bool success = false; 68 | for (int i = 0; i < k_add; i++) { 69 | int value = rand()%16; 70 | auto start = std::chrono::high_resolution_clock::now(); 71 | success = S->insert(value); 72 | time_t finish_time = time(NULL); 73 | auto wait_time = std::chrono::high_resolution_clock::now() - start; 74 | if (success) 75 | { 76 | printf("At %s: thread %d - trial number %d - inserted %d in the queue\n", 77 | currentTime(finish_time), th_id, i, value); 78 | } 79 | else 80 | { 81 | printf("At %s: DUPLICATE! thread %d - trial number %d - failed inserting %d\n", 82 | currentTime(finish_time), th_id, i, value); 83 | } 84 | 85 | microseconds_add += std::chrono::duration_cast(wait_time).count(); 86 | sleep(delay(lamda_add)); 87 | } 88 | pthread_exit(NULL); 89 | } 90 | 91 | int main() { 92 | 93 | srand(time(NULL)); 94 | freopen("inp-params.txt", "r", stdin); 95 | freopen("result-output.txt", "w", stdout); 96 | cin >> n_add >> n_remove >> k_add >> k_remove >> lamda_add >> lambda_remove; 97 | 98 | setMaxHeight((int)log2(n_add * k_add)); 99 | S = new skipListQueue(); 100 | add_created.store(false); 101 | remove_created.store(false); 102 | thread *arr_add = new thread[n_add]; 103 | for (int i=0; i