commit 36cd7d8f25befd84d2a3f614fab1b5b092f96bc0
parent 7376a2f9beca5c6c6b5c912ae19d448d100b6ab3
Author: oblique <psyberbits@gmail.com>
Date:   Tue,  2 Apr 2013 03:29:40 +0300
semaphore.h: add readers-writer lock
Diffstat:
| M | include/semaphore.h |  |  | 151 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- | 
1 file changed, 150 insertions(+), 1 deletion(-)
diff --git a/include/semaphore.h b/include/semaphore.h
@@ -71,7 +71,8 @@ semaphore_done(semaphore_t *sem)
 
 
 /* mutex is a binary semaphore */
-#define MUTEX_INIT	{ SEMAPHORE_INIT(1) }
+#define MUTEX_INIT		{ SEMAPHORE_INIT(1) }
+#define MUTEX_INIT_LOCKED	{ SEMAPHORE_INIT(0) }
 
 static inline void
 mutex_init(mutex_t *mut)
@@ -108,4 +109,152 @@ mutex_unlock(mutex_t *mut)
 	semaphore_done(&mut->sem);
 }
 
+
+/* readers-writer lock
+ * implemented with 'Passing the Baton' technique
+ *
+ * rules:
+ * 1) if we have an awakened writer or writers in the queue then
+ *    a new reader will be added in readers' queue.
+ * 2) if all awakened readers are done then we awake a writer,
+ *    even if we have readers in the queue.
+ * 3) if the awakened writer and all writers in the queue are done
+ *    then we awake all the readers. */
+typedef struct {
+	mutex_t lock;
+	mutex_t writer_wait;
+	mutex_t reader_wait;
+	u32 nr_writers;
+	u32 nr_readers;
+	u32 nr_writers_queue;
+	u32 nr_readers_queue;
+} rwlock_t;
+
+#define RWLOCK_INIT {				\
+	MUTEX_INIT,				\
+	MUTEX_INIT_LOCKED,			\
+	MUTEX_INIT_LOCKED,			\
+	0,					\
+	0,					\
+	0,					\
+	0					\
+}
+
+static inline void
+rwlock_init(rwlock_t *rwl)
+{
+	mutex_init(&rwl->lock);
+	mutex_init(&rwl->writer_wait);
+	mutex_lock(&rwl->writer_wait);
+	mutex_init(&rwl->reader_wait);
+	mutex_lock(&rwl->reader_wait);
+	rwl->nr_writers = 0;
+	rwl->nr_readers = 0;
+	rwl->nr_writers_queue = 0;
+	rwl->nr_readers_queue = 0;
+}
+
+static inline void
+rwlock_rdlock(rwlock_t *rwl)
+{
+	mutex_lock(&rwl->lock);
+	/* if we have awakened writer or writers in the queue then wait */
+	if (rwl->nr_writers > 0 || rwl->nr_writers_queue > 0) {
+		rwl->nr_readers_queue++;
+		mutex_unlock(&rwl->lock);
+		/* wait */
+		mutex_lock(&rwl->reader_wait);
+	}
+
+	rwl->nr_readers++;
+
+	/* if there is at least one reader in the queue,
+	 * awake it (i.e. awake the next reader) */
+	if (rwl->nr_readers_queue > 0) {
+		rwl->nr_readers_queue--;
+		mutex_unlock(&rwl->reader_wait);
+	} else {
+		mutex_unlock(&rwl->lock);
+	}
+}
+
+/* returns 1 if managed to lock
+ * returns 0 if not */
+static inline int
+rwlock_tryrdlock(rwlock_t *rwl)
+{
+	mutex_lock(&rwl->lock);
+	/* if we have awakened writer or writers in the queue then we can't lock */
+	if (rwl->nr_writers > 0 || rwl->nr_writers_queue > 0) {
+		mutex_unlock(&rwl->lock);
+		return 0;
+	}
+
+	rwl->nr_readers++;
+	mutex_unlock(&rwl->lock);
+	return 1;
+}
+
+static inline void
+rwlock_wrlock(rwlock_t *rwl)
+{
+	mutex_lock(&rwl->lock);
+	/* if we have an awakened writer or awakened readers then wait */
+	if (rwl->nr_writers > 0 || rwl->nr_readers > 0) {
+		rwl->nr_writers_queue++;
+		mutex_unlock(&rwl->lock);
+		/* wait */
+		mutex_lock(&rwl->writer_wait);
+	}
+
+	rwl->nr_writers++;
+	mutex_unlock(&rwl->lock);
+}
+
+/* returns 1 if managed to lock
+ * returns 0 if not */
+static inline int
+rwlock_trywrlock(rwlock_t *rwl)
+{
+	mutex_lock(&rwl->lock);
+	/* if we have an awakened writer or awakened readers then we can't lock */
+	if (rwl->nr_writers > 0 || rwl->nr_readers > 0) {
+		mutex_unlock(&rwl->lock);
+		return 0;
+	}
+
+	rwl->nr_writers++;
+	mutex_unlock(&rwl->lock);
+	return 1;
+}
+
+static inline void
+rwlock_unlock(rwlock_t *rwl)
+{
+	mutex_lock(&rwl->lock);
+
+	/* a reader is done */
+	if (rwl->nr_readers > 0) {
+		rwl->nr_readers--;
+	/* a writer is done */
+	} else if (rwl->nr_writers > 0) {
+		rwl->nr_writers--;
+	}
+
+	/* if all awakened readers are done and we have writers in the queue
+	 * then awake a writer */
+	if (rwl->nr_readers == 0 && rwl->nr_writers_queue > 0) {
+		rwl->nr_writers_queue--;
+		mutex_unlock(&rwl->writer_wait);
+	/* if the awakened writer and all writers from the queue are done
+	 * then awake a reader */
+	} else if (rwl->nr_writers == 0 && rwl->nr_writers_queue == 0 &&
+		   rwl->nr_readers_queue > 0) {
+		rwl->nr_readers_queue--;
+		mutex_unlock(&rwl->reader_wait);
+	} else {
+		mutex_unlock(&rwl->lock);
+	}
+}
+
 #endif	/* __SEMAPHORE_H */