Skip to content

Commit

Permalink
docs: add semaphore example
Browse files Browse the repository at this point in the history
Signed-off-by: Muhan Song <songmuhan@stu.pku.edu.cn>
  • Loading branch information
songmuhan committed Sep 30, 2023
1 parent 453c720 commit b1a3d9f
Showing 1 changed file with 73 additions and 0 deletions.
73 changes: 73 additions & 0 deletions tokio/src/sync/semaphore.rs
Expand Up @@ -123,6 +123,79 @@ use std::sync::Arc;
/// # }
/// ```
///
/// ## Prevent tests from running in parallel
///
/// By default, Rust parallelly runs tests in the same file. However, In some cases, running two tests in parallel may lead to some problems.
/// For example, this can happen when tests use the same database.
///
/// Consider the following scenario:
/// 1. `test_insert`: Inserts a key-value pair into the database, then retrieves the value using the same key to verify the insertion.
/// 2. `test_update`: After insertion, this test updates the key's value to a new one and verifies if the value has been accurately updated.
/// 3. `test_others`: Other tests that dosen't modify the database state, thus can run parallely with previous tests.
///
/// For this example, `test_insert` and `test_update` need to run in sequence to ensure they can consistently pass., but it's doesn't matter which one runs first.
/// Leveraging a semaphore with a single permit elegantly addresses this challenge.
///
/// ```
/// #[cfg(test)]
/// mod tests {
/// use tokio::sync::Semaphore;
///
/// // Initialize a static semaphore with only one permit, which is used to
/// // prevent test_insert and test_update from running parallely.
/// static PERMIT: Semaphore = Semaphore::const_new(1);
///
/// // Initialize the database that will be used by the subsequent tests.
/// static DB: database = database::setup();
///
/// #[tokio::test]
/// async fn test_insert() {
///
/// // Acquire permit before proceeding. Since the semaphore has only one permit,
/// // the test will wait if the permit is already acquired by other tests.
/// let permit = PERMIT.acquire().await.unwrap();
///
/// // Do the actual test stuff with database
///
/// // Insert a key-value pair to database
/// let (key, value) = ("name", 0);
/// DB.insert((key, value)).await;
///
/// // Verify if the value has been inserted correctly.
/// assert_eq!(DB.get(key), value);
///
/// // Undo the insertation, make database status unchanged.
/// DB.delete(key).await;
/// // Permit automatically dropped here
/// }
/// #[tokio::test]
/// async fn test_update() {
/// // Acquire permit first, wait until PERMIT can be acquired
/// let permit = PERMIT.acquire().await.unwrap();
///
/// // Do the same insert.
/// let (key, value) = ("name", 0);
/// DB.insert((key, value)).await;
///
/// // Update the existing value with a new one.
/// let new_value = 1;
/// DB.update((key, new_value)).await;
///
/// // Verify if the value has been updated correctly.
/// assert_eq!(DB.get(key).await, new_value);
///
/// // Undo any modificattion.
/// DB.delete(key).await;
/// // Permit automatically dropped here.
/// }
/// #[tokio::test]
/// async fn test_others() {
/// // these tests can run in parallel with test_insert and test_update.
/// // They have nothing to do with PERMIT.
/// }
/// }
/// ```
///
/// ## Rate limiting using a token bucket
///
/// Many applications and systems have constraints on the rate at which certain
Expand Down

0 comments on commit b1a3d9f

Please sign in to comment.