备注

AI Translation Notice

This document was automatically translated by Qwen/Qwen3-8B model, for reference only.

  • Source document: kernel/locking/rwlock.md

  • Translation time: 2025-05-19 01:41:57

  • Translation model: Qwen/Qwen3-8B

Please report issues via Community Channel

RwLock Read-Write Lock

备注

Author: sujintao

Email: sujintao@dragonos.org

1. Introduction

  A read-write lock is a mechanism used in a concurrent environment to protect shared data among multiple processes. Compared to a regular spinlock, a read-write lock divides access to shared data into two types: read access and write access. Read access to shared data is controlled by a read lock, while write access to shared data is controlled by a write lock. The design of a read-write lock allows for multiple “readers” (read-only access) and a single “writer” (write access) to coexist simultaneously. For shared data that is mostly read-only, using a read-write lock to control access can improve performance to some extent.

2. Implementation of Read-Write Lock in DragonOS

2.1 Mechanism of Read-Write Lock

  The purpose of a read-write lock is to maintain the consistency of shared variables in a multi-threaded system. Data is wrapped in an RwLock data structure, and all access and modification must be done through this structure. Each process that accesses shared data will obtain a guard. A read-only process obtains a READER (reader guard), while a process that needs to modify a shared variable obtains a WRITER (writer guard). As a “shadow” of the RwLock, threads perform access and modification operations based on the guard.

  In practice, in addition to READER and WRITER, a read-write lock also introduces an UPGRADER. This is a guard that lies between READER and WRITER. The role of the UPGRADER is to prevent WRITER starvation. When a process obtains an UPGRADER, it treats it as a READER. However, the UPGRADER can be upgraded, and after upgrading, it becomes a WRITER guard, allowing write operations on shared data.

  All guards satisfy the RAII mechanism native to Rust. When the scope of a guard ends, the guard will automatically release.

2.2 Relationship Between Read-Write Lock Guards

  At any given time, multiple READERS can exist, meaning that multiple processes can access shared data simultaneously. However, only one WRITER can exist at a time, and when a process obtains a WRITER, no READERS or UPGRADERS can exist. A process can obtain an UPGRADER only if there are no existing UPGRADERS or WRITERS. However, once a process obtains an UPGRADER, it cannot successfully apply for a READER.

2.3 Design Details

2.3.1 RwLock Data Structure

pub struct RwLock<T> {
    lock: AtomicU32,//原子变量
    data: UnsafeCell<T>,
}

2.3.2 READER Guard Data Structure

pub struct RwLockReadGuard<'a, T: 'a> {
    data: *const T,
    lock: &'a AtomicU32,
}

2.3.3 UPGRADER Guard Data Structure

pub struct RwLockUpgradableGuard<'a, T: 'a> {
    data: *const T,
    inner: &'a RwLock<T>,
}

2.3.4 WRITER Guard Data Structure

pub struct RwLockWriteGuard<'a, T: 'a> {
    data: *mut T,
    inner: &'a RwLock<T>,
}

2.3.5 Introduction to the lock Structure in RwLock

The lock is a 32-bit atomic variable AtomicU32, and its bit allocation is as follows:

                                                       UPGRADER_BIT     WRITER_BIT
                                                         ^                   ^
OVERFLOW_BIT                                             +------+    +-------+
  ^                                                             |    |
  |                                                             |    |
+-+--+--------------------------------------------------------+-+--+-+--+
|    |                                                        |    |    |
|    |                                                        |    |    |
|    |             The number of the readers                  |    |    |
|    |                                                        |    |    |
+----+--------------------------------------------------------+----+----+
  31  30                                                    2   1    0

  (From right to left) The 0th bit represents whether WRITER is valid. If WRITER_BIT = 1, it indicates that a process has obtained a WRITER guard. If UPGRADER_BIT = 1, it indicates that a process has obtained an UPGRADER guard. Bits 2 to 30 are used to represent the number of processes that have obtained READER guards in binary form. The 31st bit is an overflow detection bit. If OVERFLOW_BIT = 1, new requests for obtaining READER guards will be rejected.

3. Main APIs of Read-Write Lock

3.1 Main APIs of RwLock

///功能:  输入需要保护的数据类型data,返回一个新的RwLock类型.
pub const fn new(data: T) -> Self
///功能: 获得READER守卫
pub fn read(&self) -> RwLockReadGuard<T>
///功能: 尝试获得READER守卫
pub fn try_read(&self) -> Option<RwLockReadGuard<T>>
///功能: 获得WRITER守卫
pub fn write(&self) -> RwLockWriteGuard<T>
///功能: 尝试获得WRITER守卫
pub fn try_write(&self) -> Option<RwLockWriteGuard<T>>
///功能: 获得UPGRADER守卫
pub fn upgradeable_read(&self) -> RwLockUpgradableGuard<T>
///功能: 尝试获得UPGRADER守卫
pub fn try_upgradeable_read(&self) -> Option<RwLockUpgradableGuard<T>>

3.2 Main APIs of the WRITER Guard RwLockWriteGuard

///功能: 将WRITER降级为READER
pub fn downgrade(self) -> RwLockReadGuard<'rwlock, T>
///功能: 将WRITER降级为UPGRADER
pub fn downgrade_to_upgradeable(self) -> RwLockUpgradableGuard<'rwlock, T>

3.3 Main APIs of the UPGRADER Guard RwLockUpgradableGuard

///功能: 将UPGRADER升级为WRITER
pub fn upgrade(mut self) -> RwLockWriteGuard<'rwlock, T> 
///功能: 将UPGRADER降级为READER
pub fn downgrade(self) -> RwLockReadGuard<'rwlock, T>

4. Usage Examples

static LOCK: RwLock<u32> = RwLock::new(100 as u32);

fn t_read1() {
    let guard = LOCK.read();
    let value = *guard;
    let readers_current = LOCK.reader_count();
    let writers_current = LOCK.writer_count();
    println!(
        "Reader1: the value is {value}
    There are totally {writers_current} writers, {readers_current} readers"
    );
}

fn t_read2() {
    let guard = LOCK.read();
    let value = *guard;
    let readers_current = LOCK.reader_count();
    let writers_current = LOCK.writer_count();
    println!(
        "Reader2: the value is {value}
    There are totally {writers_current} writers, {readers_current} readers"
    );
}

fn t_write() {
    let mut guard = LOCK.write();
    *guard += 100;
    let writers_current = LOCK.writer_count();
    let readers_current = LOCK.reader_count();
    println!(
        "Writers: the value is {guard}
    There are totally {writers_current} writers, {readers_current} readers",
        guard = *guard
    );
    let read_guard=guard.downgrade();
    let value=*read_guard;
    println!("After downgraded to read_guard: {value}");
}

fn t_upgrade() {
    let guard = LOCK.upgradeable_read();
    let value = *guard;
    let readers_current = LOCK.reader_count();
    let writers_current = LOCK.writer_count();
    println!(
        "Upgrader1 before upgrade: the value is {value}
    There are totally {writers_current} writers, {readers_current} readers"
    );
    let mut upgraded_guard = guard.upgrade();
    *upgraded_guard += 100;
    let writers_current = LOCK.writer_count();
    let readers_current = LOCK.reader_count();
    println!(
        "Upgrader1 after upgrade: the value is {temp}
    There are totally {writers_current} writers, {readers_current} readers",
        temp = *upgraded_guard
    );
    let downgraded_guard=upgraded_guard.downgrade_to_upgradeable();
    let value=*downgraded_guard;
    println!("value after downgraded: {value}");
    let read_guard=downgraded_guard.downgrade();
    let value_=*read_guard;
    println!("value after downgraded to read_guard: {value_}");
}

fn main() {
    let r2=thread::spawn(t_read2);
    let r1 = thread::spawn(t_read1);
    let t1 = thread::spawn(t_write);
    let g1 = thread::spawn(t_upgrade);
    r1.join().expect("r1");
    t1.join().expect("t1");
    g1.join().expect("g1");
    r2.join().expect("r2");
}