-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/cmake-build-debug/CMakeFiles/clion-log.txt b/cmake-build-debug/CMakeFiles/clion-log.txt
deleted file mode 100644
index 48f35e9..0000000
--- a/cmake-build-debug/CMakeFiles/clion-log.txt
+++ /dev/null
@@ -1 +0,0 @@
-CMakeLists.txt not found in E:\Github\OSLab\OSLab-P4
diff --git a/error.h b/error.h
deleted file mode 100644
index 15547cd..0000000
--- a/error.h
+++ /dev/null
@@ -1,13 +0,0 @@
-//
-// Created by Sean on 2017/12/2.
-//
-
-#ifndef OSLAB_P4_ERROR_H
-#define OSLAB_P4_ERROR_H
-
-#define E_NO_CORRESPONDING_VALUE 1551
-#define E_DATA_OVERFLOW 1919
-
-int user_error;
-
-#endif //OSLAB_P4_ERROR_H
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..2aa3450
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.8)
+project(P4)
+
+set(CMAKE_CXX_STANDARD 11)
+
+find_package (Threads)
+set(SOURCE_FILES main.c lock.h counter.h list.h hash.h lock.c counter.c list.c hash.c)
+add_executable(P4 ${SOURCE_FILES})
+target_link_libraries (P4 ${CMAKE_THREAD_LIBS_INIT})
\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..037fcf4
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,14 @@
+make: libcounter.so liblist.so libhash.so
+
+P4:
+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
+ cc -lcounter -llist -lhash -L. -o P4 main.c libcounter.so liblist.so libhash.so -lpthread
+
+libcounter.so:
+ cc -shared -fPIC counter.c lock.h lock.c -o libcounter.so
+
+liblist.so:
+ cc -shared -fPIC list.c lock.h lock.c -o liblist.so
+
+libhash.so:
+ cc -shared -fPIC hash.c list.h list.c lock.h lock.c -o libhash.so
diff --git a/src/counter.c b/src/counter.c
index 2c4e792..9068819 100644
--- a/src/counter.c
+++ b/src/counter.c
@@ -1,23 +1,21 @@
#include "counter.h"
-extern int user_error;
-
/**
- * Initialization part of counter
- * @param c a pointer to a counter
+ * Initialization part of counter
+ * @param c pointer to a counter
* @param value Initial value of the counter
*/
-void counter_init(counter_t* c, int value) {
+void counter_init(counter_t *c, int value) {
c->value = value;
lock_init(&c->lock);
}
/**
- * Get the value of the counter
- * @param c a pointer to a counter
- * @return the value of the counter
+ * Get the value of the counter
+ * @param c pointer to a counter
+ * @return the current value of the counter
*/
-int counter_get_value(counter_t* c) {
+int counter_get_value(counter_t *c) {
#if defined(LOCK_RWLOCK)
rwlock_rdlock(&c->lock);
#else
@@ -29,37 +27,29 @@ int counter_get_value(counter_t* c) {
}
/**
- * Make the counter's value increase by 1
- * @param c a pointer to a counter
+ * Increase the counter by 1
+ * @param c pointer to a counter
*/
-void counter_increment(counter_t* c) {
+void counter_increment(counter_t *c) {
#if defined(LOCK_RWLOCK)
rwlock_wrlock(&c->lock);
#else
lock_acquire(&c->lock);
#endif
- if (c->value == INT_MAX) {
- user_error = E_DATA_OVERFLOW;
- }else {
- c->value++;
- }
+ c->value++;
lock_release(&c->lock);
}
/**
- * Make the counter's value increase by 1
- * @param c a pointer to a counter
+ * Decrease the counter by 1
+ * @param c pointer to a counter
*/
-void counter_decrement(counter_t* c) {
+void counter_decrement(counter_t *c) {
#if defined(LOCK_RWLOCK)
rwlock_wrlock(&c->lock);
#else
lock_acquire(&c->lock);
#endif
- if (c->value == INT_MIN) {
- user_error = E_DATA_OVERFLOW;
- }else {
- c->value--;
- }
+ c->value--;
lock_release(&c->lock);
}
diff --git a/src/counter.h b/src/counter.h
index b4643fd..d61fb4d 100644
--- a/src/counter.h
+++ b/src/counter.h
@@ -2,10 +2,10 @@
#define P4_COUNTER_H
#include "lock.h"
-#define INT_MAX 2147483647
-#define INT_MIN (-INT_MAX - 1)
+
/**
* A concurrent counter type
+ * All operations except initialization are thread-safe
*/
typedef struct {
int value; /**< internal counter variable */
diff --git a/src/hash.c b/src/hash.c
index 39e2ce2..420adbe 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -1,26 +1,11 @@
#include "hash.h"
/**
- * Initialize hash table with given bucket size
- * will call perror to print error message once malloc fail
+ * Initialize the hash table with given bucket size
* @param hash A pointer to hash table
* @param size designated bucket size
- *//*
-void hash_init(hash_t *hash, int size, unsigned int threshold) { // hash_init with threshold
- hash->lists = malloc(sizeof(list_t)*size);
- if (hash->lists == NULL) {
- perror("malloc");
- return;
- }
- int i;
- hash->bucket_size = size;
- hash->threshold = threshold;
- for (i = 0; i < size; i++) {
- list_init(&hash->lists[i]);
- }
- //lock_init(&hash->lock);
-}*/
-void hash_init(hash_t *hash, int size) { // hash_init without threshold
+ */
+void hash_init(hash_t *hash, int size) {
int i;
hash->bucket_size = size;
hash->lists = malloc(sizeof(list_t)*size);
@@ -42,7 +27,7 @@ void hash_insert(hash_t *hash, unsigned int key) {
/**
* Delete a key into hash table
- * if multiple keys detected in hash table, only delete one of them
+ * If multiple keys detected in hash table, only delete one of them
* @param hash The pointer to hash table
* @param key The key to be deleted
*/
@@ -55,7 +40,7 @@ void hash_delete(hash_t *hash, unsigned int key) {
* Find a given key in hash table
* @param hash The pointer to hash table
* @param key The key to find
- * @return A pointer to node contains given key, should a into node_t before use.
+ * @return A pointer to the node with given key, should cast to node_t type before use.
*/
void* hash_lookup(hash_t *hash, unsigned int key) {
int bucket = key % hash->bucket_size;
@@ -63,7 +48,7 @@ void* hash_lookup(hash_t *hash, unsigned int key) {
}
/**
- * remove an given hash table and free the pointer of it
+ * Remove an given hash table and free the pointer of it
* @param hash The pointer to hash table
*/
void* hash_destroy(hash_t *hash) {
@@ -73,32 +58,3 @@ void* hash_destroy(hash_t *hash) {
}
free(hash->lists);
}
-
-/**
- * Check whether the total length of the hash table has exceeded the threshold
- * if so resize the hash table and destory the previous one
- * @param hash The pointer to a hash table
- */
-void hash_resize(hash_t *hash) {
- unsigned int i, tmp_key, length = 0;
- for (i = 0; i < hash->bucket_size; ++i) {
- length += list_count(&hash->lists[i]);
- }
- if (length < hash->threshold) {
- return;
- }
- hash_t new_hash;
- hash_init(&new_hash, length*2, 2*hash->threshold);
- for (i = 0; i < hash->bucket_size; ++i) {
- list_t tmp_list = hash->lists[i];
- node_t *cur = tmp_list.head;
- while (cur != NULL) {
- tmp_key = cur->key;
- hash_insert(&new_hash, tmp_key);
- cur = cur->next;
- }
- }
- hash_t* hash_old = hash;
- hash = &new_hash;
- hash_destroy(hash_old);
-}
\ No newline at end of file
diff --git a/src/hash.h b/src/hash.h
index aaa4c9a..04681e9 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -3,19 +3,21 @@
#include "list.h"
+/**
+ * The concurrent hash definition
+ * The hash function is simply module bucket size (but effective)
+ * This implementation need no more parallel protection since list is already thread-safe
+ * All operations except initialize and destroy are thread-safe
+ */
typedef struct {
- list_t *lists;
- int bucket_size;
- //lock_t lock;
- //unsigned int threshold;
+ list_t *lists; /**< lists for hash buckets */
+ int bucket_size; /**< the bucket size designated when initialized, can't be changed during use */
} hash_t;
-//void hash_init(hash_t *hash, int size, unsigned int threshold);
void hash_init(hash_t *hash, int size);
void hash_insert(hash_t *hash, unsigned int key);
void hash_delete(hash_t *hash, unsigned int key);
void *hash_lookup(hash_t *hash, unsigned int key);
void* hash_destroy(hash_t *hash);
-//void hash_resize(hash_t *hash);
#endif //P4_HASH_H
diff --git a/src/list.c b/src/list.c
index 5e3642a..7325b3f 100644
--- a/src/list.c
+++ b/src/list.c
@@ -1,7 +1,4 @@
#include "list.h"
-#include
-
-extern int user_error;
/**
* Initialize the given list
@@ -19,38 +16,29 @@ void list_init(list_t *list) {
*/
void list_insert(list_t *list, unsigned int key) {
node_t *new_node = malloc(sizeof(node_t));
- if (newNode == NULL) {
- perror("malloc");
- return;
- }
new_node->key = key;
- //printf("%ld In insert1\n", pthread_self()%10000);
-#if defined(LOCK_RWLOCK)
+#if defined(LOCK_RWLOCK) || defined(LOCK_PRWLOCK)
rwlock_wrlock(&list->lock);
#else
lock_acquire(&list->lock);
#endif
- //printf("%ld In insert2\n", pthread_self()%10000);
new_node->next = list->head;
list->head = new_node;
- //printf("%ld Out insert1\n", pthread_self()%10000);
lock_release(&list->lock);
- //printf("%ld Out insert2\n", pthread_self()%10000);
}
/**
- * Delete the node with value key
+ * Delete one node with the given value
+ * If multiple targets are found, only delete one of them
* @param list A pointer to a list
* @param key The key value of the node to be deleted
*/
void list_delete(list_t* list, unsigned int key) {
- //printf("%ld In delete1\n", pthread_self()%10000);
-#if defined(LOCK_RWLOCK)
+#if defined(LOCK_RWLOCK) || defined(LOCK_PRWLOCK)
rwlock_wrlock(&list->lock);
#else
lock_acquire(&list->lock);
#endif
- //printf("%ld In delete2\n", pthread_self()%10000);
node_t* cur = list->head;
node_t* pre = NULL;
while (cur != NULL) {
@@ -67,30 +55,25 @@ void list_delete(list_t* list, unsigned int key) {
list->head = cur->next;
}
free(cur);
- }else {
- user_error = E_NO_CORRESPONDING_VALUE;
}
- //printf("%ld out delete1\n", pthread_self()%10000);
lock_release(&list->lock);
- //printf("%ld out delete2\n", pthread_self()%10000);
}
/**
- * Find out whether there is a node with a value of key.
- * If there is, return the pointer to the node with value key,
- * return NULL otherwise
+ * Find out whether there exists a node with the given key.
+ * If found, return a pointer to the target node.
+ * Otherwise, return NULL instead.
+ *
* @param list A pointer to a list
* @param key The key value of the node to be lookup
- * @return A pointer to the node with value key
+ * @return A pointer to the node with given key, should cast to node_t type before use.
*/
void* list_lookup(list_t* list, unsigned int key) {
-#if defined(LOCK_RWLOCK)
+#if defined(LOCK_RWLOCK) || defined(LOCK_PRWLOCK)
rwlock_rdlock(&list->lock);
#else
lock_acquire(&list->lock);
#endif
- //printf("%ld In lookup1\n", pthread_self()%10000);
- //printf("%ld In lookup2\n", pthread_self()%10000);
node_t* cur = list->head;
while (cur != NULL) {
if (cur->key == key) {
@@ -98,19 +81,17 @@ void* list_lookup(list_t* list, unsigned int key) {
}
cur = cur->next;
}
- //printf("%ld Out lookup1\n", pthread_self()%10000);
lock_release(&list->lock);
- //printf("%ld Out lookup2\n", pthread_self()%10000);
return cur;
}
/**
- * Calculate the total number of nodes in list
+ * Calculate the total number of nodes in this list
* @param list A pointer to a list
- * @return the total number of nodes in list
+ * @return The total number of nodes in the list
*/
int list_count(list_t* list) {
-#if defined(LOCK_RWLOCK)
+#if defined(LOCK_RWLOCK) || defined(LOCK_PRWLOCK)
rwlock_rdlock(&list->lock);
#else
lock_acquire(&list->lock);
@@ -118,9 +99,6 @@ int list_count(list_t* list) {
int cnt = 0;
node_t *cur = list->head;
while (cur != NULL) {
- if (cnt == INT_MIN) {
- user_error = E_DATA_OVERFLOW;
- }
cnt++;
cur = cur->next;
}
@@ -129,12 +107,12 @@ int list_count(list_t* list) {
}
/**
- * Calculate the sum of all nodes' value
+ * Calculate the sum of all nodes' key field
* @param list A pointer to a list
- * @return the sum of all nodes' value
+ * @return The sum of all nodes' value
*/
long long list_sum(list_t* list) {
-#if defined(LOCK_RWLOCK)
+#if defined(LOCK_RWLOCK) || defined(LOCK_PRWLOCK)
rwlock_rdlock(&list->lock);
#else
lock_acquire(&list->lock);
@@ -150,11 +128,11 @@ long long list_sum(list_t* list) {
}
/**
- * Destroy the given list
- * @param list A pointer to a list
+ * Destroy the given list
+ * @param list The list to be deleted
*/
void list_destroy(list_t* list) {
-#if defined(LOCK_RWLOCK)
+#if defined(LOCK_RWLOCK) || defined(LOCK_PRWLOCK)
rwlock_wrlock(&list->lock);
#else
lock_acquire(&list->lock);
diff --git a/src/list.h b/src/list.h
index 0648e73..8f8b19f 100644
--- a/src/list.h
+++ b/src/list.h
@@ -4,16 +4,25 @@
#include "lock.h"
#include
#include
-#define INT_MAX 2147483647
+/**
+ * Node type in the list
+ *
+ */
typedef struct _node_t {
- unsigned int key;
- struct _node_t *next;
+ unsigned int key; /**< the key field of this node */
+ struct _node_t *next; /**< pointer to the next node in the list */
} node_t;
+
+/**
+ * A concurrent list definition
+ * All operations except initialization and destroy are thread-safe
+ * Maintain a head-insert linked-list
+ */
typedef struct {
- node_t *head;
- lock_t lock;
+ node_t *head; /**< a pointer to the head node */
+ lock_t lock; /**< guarantee sequential execution in list functions */
} list_t;
void list_init(list_t *list);
diff --git a/src/lock.c b/src/lock.c
index 81e4ab2..2f0a232 100644
--- a/src/lock.c
+++ b/src/lock.c
@@ -8,25 +8,27 @@
// struct timespec wait_time = { 1, 0 };
+#define atomic_add(P, V) __sync_add_and_fetch((P), (V))
+
+
static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3) {
return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
}
+/**
+ * Pause the cpu core using assembly code to avoid bad performance on some machine
+ */
static inline void cpu_pause(void) {
- asm volatile("rep; nop" : : : "memory");
- //asm volatile("pause\n": : :"memory");
+ //asm volatile("rep; nop" : : : "memory");
+ asm volatile("pause\n": : :"memory");
}
-/*
-static inline uint xchg(volatile unsigned int *addr, unsigned int newval) {
- uint result;
- asm volatile("lock; xchgl %0, %1"
- : "+m" (*addr), "=a" (result)
- : "1" (newval)
- : "cc");
- return result;
-}*/
-
+/**
+ * Exchange value in addr and newval atomically
+ * @param addr The
+ * @param newval
+ * @return
+ */
static inline unsigned xchg(void *addr, unsigned newval) {
asm volatile("xchgl %0, %1"
: "=r" (newval)
@@ -44,45 +46,80 @@ static inline uint cmpxchg(volatile unsigned int *addr, unsigned int oldval, uns
return ret;
}
+/**
+ * Initialize a spin-lock, should be called before use
+ * @param lock Pointer to the spin-lock need to be initialized
+ */
void spinlock_init(spinlock_t* lock) {
- lock->flag = 0;
+ *lock = 0;
}
+/**
+ * Try to acquire the given spin-lock
+ * If the lock currently not available, this function will keep trying until somebody release the lock
+ * @param lock Pointer to the spin-lock want to obtain
+ */
void spinlock_acquire(spinlock_t *lock) {
- while (xchg(&lock->flag, 1) == 1) {
+ while (xchg(lock, 1) == 1) {
cpu_pause(); // spin-wait
}
}
+/**
+ * Release the given spin-lock
+ * Notice that release a lock not hold by itself will cause unpredictable result
+ * @param lock Pointer to the spin-lock want to release
+ */
void spinlock_release(spinlock_t *lock) {
- lock->flag = 0;
+ *lock = 0;
}
+/**
+ * Initialize a mutex, should be called before use
+ * @param lock Pointer to the mutex need to be initialized
+ */
void mutex_init(mutex_t *lock) {
*lock = 0;
}
+/**
+ * Try to acquire the given mutex
+ * A failed try will cause the thread sleep until another thread wake it by releasing this mutex
+ * @param lock Pointer to the mutex want to obtain
+ */
void mutex_acquire(mutex_t *lock) {
int value = xchg(lock, 1);
while (value) {
- //printf("%d lock rd: %d\n", pthread_self()%10000, value);
sys_futex(lock, FUTEX_WAIT_PRIVATE, 1, NULL, NULL, 0);
value = xchg(lock, 1);
}
- //printf("%d locked\n", pthread_self()%10000);
}
+/**
+ * Release the given mutex
+ * Notice that release a lock not hold by itself will cause unpredictable result
+ * @param lock Pointer to the spin-lock want to release
+ */
void mutex_release(mutex_t *lock) {
- while (xchg(lock, 0)) ;
- //printf("%d unlock : %d\n", pthread_self()%10000, *lock);
+ xchg(lock, 0);
sys_futex(lock, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
}
+/**
+ * Initialize a two-phase lock, should be called before use
+ * @param lock Pointer to the lock need to be initialized
+ */
void twophase_init(twophase_t *lock) {
*lock = 0;
}
-#define LOOP_MAX 1000
+/**
+ * Try to acquire the given two-phase lock
+ * Wait for some one to release first, the wait time is designated by the LOOP_MAX macro (defined below)
+ * If the lock still acquired by someone else after wait phase, it will sleep until someone release the lock
+ * @param lock Pointer to the two-phase lock want to obtain
+ */
+#define LOOP_MAX 100
void twophase_acquire(twophase_t *lock) {
int i, value = 1;
for (i = 0; i < LOOP_MAX; i++) {
@@ -92,16 +129,22 @@ void twophase_acquire(twophase_t *lock) {
}
cpu_pause();
}
+
if (value == 1) {
value = xchg(lock, 2);
}
-
while (value) {
sys_futex(lock, FUTEX_WAIT_PRIVATE, 2, NULL, NULL, 0);
value = xchg(lock, 2);
}
}
+/**
+ * Release the given two-phase lock
+ * In the first phase it will try to give the lock to someone awake
+ * If failed, it will wake up a sleeping thread on this lock
+ * @param lock Pointer to the two-phase lock want to release
+ */
void twophase_release(twophase_t *lock) {
int i;
@@ -112,56 +155,83 @@ void twophase_release(twophase_t *lock) {
}
for (i = 0; i < LOOP_MAX; i++) {
- if (*lock && cmpxchg(lock, 1, 2)) {
- return;
+ if (*lock) {
+ if (cmpxchg(lock, 1, 2)) {
+ return;
+ }
}
+ cpu_pause();
}
sys_futex(lock, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
}
-
-void cond_init(cond_t* lock) {
- lock->seq = 0;
- lock->mutex = NULL;
+/**
+ * Initialize a condition variable, should be called before use
+ * @param cv Pointer to the condition variable need to be initialized
+ */
+void cond_init(cond_t* cv) {
+ cv->seq = 0;
+ cv->mutex = NULL;
}
-void cond_wait(cond_t* lock, mutex_t* mutex) {
- int old_seq = lock->seq;
-
- if (lock->mutex != mutex) {
- if (lock->mutex != NULL) { // TODO: ERROR
+/**
+ * Wait a thread on the given condition variable
+ * The caller will sleep on the given cv, its mutex will be released
+ * Note that the mutex should be two-phase type since cv need extra information in mutex
+ * @param cv Pointer the the condition variable to wait on
+ * @param mutex The mutex owned by caller, will be released
+ */
+void cond_wait(cond_t* cv, twophase_t* mutex) {
+ int old_seq = cv->seq;
+
+ if (cv->mutex != mutex) {
+ if (cv->mutex != NULL) { // TODO: ERROR
return;
}
- cmpxchg(&lock->mutex, NULL, mutex);
- if (lock->mutex != mutex) { // TODO: ERROR
+ cmpxchg(&cv->mutex, NULL, mutex);
+ if (cv->mutex != mutex) { // TODO: ERROR
return;
}
}
- mutex_release(mutex);
+ twophase_release(mutex);
- sys_futex(&lock->seq, FUTEX_WAIT, old_seq, NULL, NULL, 0);
+ sys_futex(&cv->seq, FUTEX_WAIT_PRIVATE, old_seq, NULL, NULL, 0);
while (xchg(mutex, 2)) {
- sys_futex(mutex, FUTEX_WAIT, 2, NULL, NULL, 0);
+ sys_futex(mutex, FUTEX_WAIT_PRIVATE, 2, NULL, NULL, 0);
}
}
-void cond_signal(cond_t* lock) {
- lock->seq++;
- sys_futex(&lock->seq, FUTEX_WAKE, 1, NULL, NULL, 0);
+/**
+ * Wake up a thread waiting on the given condition variable
+ * @param cv The condition variable to receive a signal
+ */
+void cond_signal(cond_t* cv) {
+ atomic_add(&cv->seq, 1);
+ sys_futex(&cv->seq, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
}
-void cond_broadcast(cond_t* lock) {
- mutex_t* old_mutex = lock->mutex;
+/**
+ * Wake up ALL threads waiting on the given condition variable
+ * @param cv The condition variable to receive a broadcast
+ */
+void cond_broadcast(cond_t* cv) {
+ mutex_t* old_mutex = cv->mutex;
if (old_mutex == NULL) {
return;
}
- lock->seq++;
- sys_futex(&lock->seq, FUTEX_REQUEUE, 1, (void*)2147483647, old_mutex, 0);
+ atomic_add(&cv->seq, 1);
+
+ // wake up 1 waiting thread, requeue other threads on mutex to avoid context switch
+ sys_futex(&cv->seq, FUTEX_REQUEUE_PRIVATE, 1, (void*) 0x0FFFFFFF, old_mutex, 0);
}
+/**
+ * Initialize a read-write lock, should be called before use
+ * @param lock Pointer to the read-write lock need to be initialized
+ */
void rwlock_init(rwlock_t* lock) {
#if defined(LOCK_PRWLOCK)
pthread_rwlock_init(lock, NULL);
@@ -170,116 +240,82 @@ void rwlock_init(rwlock_t* lock) {
lock->writers = 0;
lock->read_waiters = 0;
lock->write_waiters = 0;
- mutex_init(&lock->mutex);
-
- lock->writer_preferred = 1; // enable this to prefer writer
-#elif defined(LOCK_RWLOCK2)
- lock->readers = 0;
- lock->writers = 0;
- lock->read_waiters = 0;
- lock->write_waiters = 0;
- mutex_init(&lock->mutex);
+ twophase_init(&lock->mutex);
cond_init(&lock->reader_lock);
cond_init(&lock->writer_lock);
#endif
}
+/**
+ * Acquire the read-end of the given read-write lock
+ * @param lock Pointer to the read-write lock to be acquired
+ */
void rwlock_rdlock(rwlock_t *lock) {
#if defined(LOCK_PRWLOCK)
pthread_rwlock_rdlock(lock);
#elif defined(LOCK_RWLOCK)
- mutex_acquire(&lock->mutex);
- while (lock->writers || (lock->write_waiters && lock->writer_preferred)) {
- lock->read_waiters++;
- mutex_release(&lock->mutex);
- sys_futex(&lock->read_waiters, FUTEX_WAIT_PRIVATE, 1, NULL, NULL, 0);
- mutex_acquire(&lock->mutex);
- lock->read_waiters--;
- }
- lock->readers++;
- mutex_release(&lock->mutex);
-#elif defined(LOCK_RWLOCK2)
- mutex_acquire(&lock->mutex);
+ twophase_acquire(&lock->mutex);
while (lock->writers || lock->write_waiters) {
lock->read_waiters++;
cond_wait(&lock->reader_lock, &lock->mutex);
lock->read_waiters--;
}
lock->readers++;
- mutex_release(&lock->mutex);
+ twophase_release(&lock->mutex);
#endif
}
+/**
+ * Acquire the write-end of the given read-write lock
+ * @param lock Pointer to the read-write lock to be acquired
+ */
void rwlock_wrlock(rwlock_t *lock) {
#if defined(LOCK_PRWLOCK)
pthread_rwlock_wrlock(lock);
#elif defined(LOCK_RWLOCK)
- mutex_acquire(&lock->mutex);
- while (lock->readers || lock->writers) {
- lock->write_waiters++;
- mutex_release(&lock->mutex);
- sys_futex(&lock->write_waiters, FUTEX_WAIT_PRIVATE, 1, NULL, NULL, 0);
- mutex_acquire(&lock->mutex);
- lock->write_waiters--;
- }
- lock->writers = 1;
- mutex_release(&lock->mutex);
-#elif defined(LOCK_RWLOCK2)
- mutex_acquire(&lock->mutex);
+ twophase_acquire(&lock->mutex);
while (lock->readers || lock->writers) {
lock->write_waiters++;
cond_wait(&lock->writer_lock, &lock->mutex);
lock->write_waiters--;
}
lock->writers = 1;
- mutex_release(&lock->mutex);
+ twophase_release(&lock->mutex);
#endif
}
+/**
+ * Release the read-write lock, for both read and write end according to POSIX standard
+ * Note this implement slightly favour the writers
+ * @param lock Pointer to the read-write to be released
+ */
void rwlock_unlock(rwlock_t* lock) {
#if defined(LOCK_PRWLOCK)
pthread_rwlock_unlock(lock);
#elif defined(LOCK_RWLOCK)
- mutex_acquire(&lock->mutex);
- if (lock->readers) { // if there're readers
+ twophase_acquire(&lock->mutex);
+ if (lock->readers) {
lock->readers--;
- if (lock->readers) { // if still have other readers
- mutex_release(&lock->readers);
- return;
+ if (lock->write_waiters) {
+ cond_signal(&lock->writer_lock);
}
- } else {
+ } else if (lock->writers) {
lock->writers = 0;
- }
- mutex_release(&lock->mutex);
-
- if (lock->write_waiters) { // wake a writer first
- sys_futex(&lock->write_waiters, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
- } else if (lock->read_waiters) { // wake all readers
- sys_futex(&lock->read_waiters, FUTEX_WAKE_PRIVATE, 2147483647, NULL, NULL, 0);
- }
-
-#elif defined(LOCK_RWLOCK2)
- mutex_acquire(&lock->mutex);
- if (lock->readers) { // if there're readers
- lock->readers--;
- if (lock->readers) { // if still have other readers
- mutex_release(&lock->readers);
- return;
+ if (lock->write_waiters) {
+ cond_signal(&lock->writer_lock);
+ } else if (lock->read_waiters) {
+ cond_broadcast(&lock->reader_lock);
}
- } else {
- lock->writers = 0;
- }
- mutex_release(&lock->mutex);
-
- if (lock->write_waiters) { // wake a writer first
- cond_signal(&lock->writer_lock);
- } else if (lock->read_waiters) { // wake all readers
- cond_broadcast(&lock->reader_lock);
}
+ twophase_release(&lock->mutex);
#endif
}
-
+/**
+ * The generic lock initialization method, not use macro for the debug consideration
+ * The lock type is designated by the macros defined in header file
+ * @param lock A pointer to the lock to be initialized
+ */
void lock_init(lock_t* lock) {
#if defined(LOCK_MUTEX)
mutex_init(lock);
@@ -289,11 +325,16 @@ void lock_init(lock_t* lock) {
twophase_init(lock);
#elif defined(LOCK_PTHREAD)
pthread_mutex_init(lock, NULL);
-#elif defined(LOCK_RWLOCK)
+#elif defined(LOCK_RWLOCK) || defined(LOCK_PRWLOCK)
rwlock_init(lock);
#endif
}
+/**
+ * The generic lock acquire method
+ * Note that read-write locks DO NOT call this function
+ * @param lock A pointer to the lock to be acquired
+ */
void lock_acquire(lock_t* lock) {
#if defined(LOCK_MUTEX)
mutex_acquire(lock);
@@ -306,6 +347,10 @@ void lock_acquire(lock_t* lock) {
#endif
}
+/**
+ * The generic lock release method
+ * @param lock A pointer to the lock to be released
+ */
void lock_release(lock_t* lock) {
#if defined(LOCK_MUTEX)
mutex_release(lock);
@@ -315,7 +360,7 @@ void lock_release(lock_t* lock) {
twophase_release(lock);
#elif defined(LOCK_PTHREAD)
pthread_mutex_unlock(lock);
-#elif defined(LOCK_RWLOCK)
+#elif defined(LOCK_RWLOCK) || defined(LOCK_PRWLOCK)
rwlock_unlock(lock);
#endif
}
diff --git a/src/lock.h b/src/lock.h
index f3df0c6..2b58609 100644
--- a/src/lock.h
+++ b/src/lock.h
@@ -3,67 +3,63 @@
#include
-/* The following 4 lock types are exclusive
- * select one of them and comment others to enable specific lock implement
- * note that rwlock will not implement lock_acquire and lock_release
- * rwlock also rewrite implements of counter and list
+/**
+ * The following 6 lock types are exclusive
+ * Select one of them and comment others to enable specific implementation for generic lock
+ * Note that rwlock will not implement lock_acquire, but rewrite implements of counter and list instead
+ * Also note that pthread rwlock rewrite rwlock implement
*/
-//#define LOCK_MUTEX
//#define LOCK_SPIN
+//#define LOCK_MUTEX
//#define LOCK_TWOPHASE
-//#define LOCK_RWLOCK
-#define LOCK_RWLOCK2
+#define LOCK_RWLOCK
//#define LOCK_PTHREAD
//#define LOCK_PRWLOCK
-typedef struct {
- unsigned int flag;
-} spinlock_t;
+/**
+ * The following 3 lock type definitions are trivial, just literal meaning
+ */
+typedef unsigned int spinlock_t;
typedef unsigned int mutex_t;
typedef unsigned int twophase_t;
-/*
-typedef union {
- unsigned int value;
- struct {
- unsigned char locked;
- unsigned char contended;
- } bit_set;
-} twophase_t;
-*/
-
+/**
+ * Condition variable is a kind of synchronization means
+ * Can cause threads wait on a specific condition
+ * Also can wake one or all waiting thread(s) easily
+ */
typedef struct {
- unsigned seq;
- mutex_t *mutex;
+ unsigned seq; /**< sequence number used to judge cv status */
+ twophase_t *mutex; /**< serialize operations on cv */
} cond_t;
+/**
+ * Read-write lock is designed to parallel read threads instead of sequential execution
+ * This implement use condition variable to wait/wake and requeue sleeping threads
+ */
#if defined(LOCK_PRWLOCK)
typedef pthread_rwlock_t rwlock_t;
#elif defined(LOCK_RWLOCK)
typedef struct {
- mutex_t mutex;
- char writer_preferred;
- unsigned readers;
- unsigned writers;
- unsigned read_waiters;
- unsigned write_waiters;
-} rwlock_t;
-#elif defined(LOCK_RWLOCK2)
-typedef struct {
- mutex_t mutex;
- cond_t reader_lock;
- cond_t writer_lock;
- unsigned readers;
- unsigned writers;
- unsigned read_waiters;
- unsigned write_waiters;
+ twophase_t mutex; /**< serialize operations on rwlock */
+ cond_t reader_lock; /**< the cv for readers */
+ cond_t writer_lock; /**< the cv for writers */
+ unsigned readers; /**< reader counter */
+ unsigned writers; /**< writer counter, should only be 0 or 1 */
+ unsigned read_waiters; /**< counter for waiters on the read end */
+ unsigned write_waiters; /**< counter for waiters on the write end */
} rwlock_t;
#else
typedef int rwlock_t;
#endif
+
+/**
+ * The generic lock type definition
+ * Directly use existing lock types
+ */
#if defined(LOCK_MUTEX)
typedef mutex_t lock_t;
#elif defined(LOCK_SPIN)
@@ -72,8 +68,6 @@ typedef spinlock_t lock_t;
typedef twophase_t lock_t;
#elif defined(LOCK_RWLOCK)
typedef rwlock_t lock_t;
-#elif defined(LOCK_RWLOCK2)
-typedef rwlock_t lock_t;
#elif defined(LOCK_PTHREAD)
typedef pthread_mutex_t lock_t;
#elif defined(LOCK_PRWLOCK)
@@ -94,10 +88,10 @@ void twophase_init(twophase_t *lock);
void twophase_acquire(twophase_t *lock);
void twophase_release(twophase_t *lock);
-void cond_init(cond_t* lock);
-void cond_wait(cond_t* lock, mutex_t* mutex);
-void cond_signal(cond_t* lock);
-void cond_broadcast(cond_t* lock);
+void cond_init(cond_t* cv);
+void cond_wait(cond_t* cv, twophase_t* mutex);
+void cond_signal(cond_t* cv);
+void cond_broadcast(cond_t* cv);
void rwlock_init(rwlock_t* self);
void rwlock_rdlock(rwlock_t *lock);
diff --git a/src/main.c b/src/main.c
index fd99a64..85763d6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,8 +19,8 @@ double endTimer() {
return (tve.tv_sec - tvb.tv_sec) * 1000 + (tve.tv_usec - tvb.tv_usec) / 1000.0;
}
-#define THREAD_COUNT 8
-#define MAX_N 10000
+#define THREAD_COUNT 4
+#define MAX_N 100000
#define SEED 1234
counter_t counter;
@@ -41,9 +41,9 @@ void* test(void* args) {
void* test2(void* args) {
printf("thread %d to sleep.\n", (int)args);
- mutex_acquire(&mutex);
+ twophase_acquire(&mutex);
cond_wait(&cond, &mutex);
- mutex_release(&mutex);
+ twophase_release(&mutex);
test_var = test_var + 1;
printf("thread %d waked.\n", (int)args);
return NULL;
@@ -69,6 +69,7 @@ void* test4(void* args) {
srand(SEED);
for (i = 0; i < MAX_N; i++) {
int rd = rand() % 100;
+ //pthread_mutex_lock(&p_mutex);
if (rd < READ_RATE) {
list_lookup(&list, rand() % RANGE);
} else if (rd < READ_RATE + INSERT_RATE) {
@@ -88,6 +89,7 @@ void* test4(void* args) {
pthread_mutex_unlock(&p_mutex);
}
}
+ //pthread_mutex_unlock(&p_mutex);
}
return NULL;
}
@@ -126,6 +128,7 @@ void main1() {
void main2() {
int i;
test_var = 0;
+ mutex_init(&mutex);
cond_init(&cond);
pthread_t* threads = malloc(sizeof(pthread_t)*THREAD_COUNT);
startTimer();